Compare commits
1 Commits
master
...
freno-dev/
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ff31346ff |
@@ -1,79 +0,0 @@
|
||||
# Remote SSH copy — HELD, do not publish
|
||||
|
||||
**Status:** removed from the `freno-dev` source on 2026-10-29 in the FRE-323
|
||||
correction, and **not yet off the live site** — the deploy is blocked on a write
|
||||
credential to `mikefreno/freno-dev` (card `82d1e1b7`). `nook.freno.me` still
|
||||
serves every sentence below until that push or a Vercel deploy lands.
|
||||
**Owner:** CMO. **Verified by:** CTO, on the FRE-320/FRE-205 work.
|
||||
**Restore only when:** FRE-320/FRE-205 ship a genuinely one-click install, the CTO
|
||||
verifies that flow, and a recording shows a remote agent joining the island.
|
||||
Until then this file is the only home for these sentences.
|
||||
|
||||
## What was removed
|
||||
|
||||
### 1. Landing collage tile
|
||||
|
||||
Source: `src/components/nook/FeatureCollage.tsx` (removed 2026-10-29).
|
||||
|
||||
```
|
||||
kicker: Remote
|
||||
title: Agents on other machines
|
||||
body: Run agents on Linux boxes and SSH servers — one click installs the
|
||||
nook-hook, and their sessions join the same island.
|
||||
```
|
||||
|
||||
The mock attached to it showed two session rows whose agent chips named
|
||||
`build-box` and `gpu-rig`.
|
||||
|
||||
### 2. Landing feature breakdown section
|
||||
|
||||
Source: `src/components/nook/FeatureBreakdowns.tsx` (removed 2026-10-29).
|
||||
|
||||
```
|
||||
kicker: Remote
|
||||
title: Any SSH box, one click away
|
||||
body: Pick a host from your ssh config or your tailnet and click once — the
|
||||
nook-hook installs itself and its agents appear beside your local ones,
|
||||
indistinguishable.
|
||||
|
||||
bullets:
|
||||
- One click: connect, push the hook, configure agents, check it can reach this Mac
|
||||
- Hosts mirror this Mac's installed integrations automatically
|
||||
- Remote sessions are ordinary rows; the agent chip names the machine
|
||||
```
|
||||
|
||||
The section's demo animated a `gpu-rig` host moving from "Not set up" through
|
||||
`connecting` → `pushing the hook binary` → `configuring agents` → `checking the
|
||||
host can reach this Mac` → `Live`, then showed a remote session row.
|
||||
|
||||
### 3. Approvals bullet
|
||||
|
||||
Source: `src/components/nook/FeatureBreakdowns.tsx` (removed 2026-10-29).
|
||||
|
||||
```
|
||||
- Works across every hooked agent, local or remote
|
||||
```
|
||||
|
||||
### 4. Feature chip
|
||||
|
||||
Source: `src/components/nook/IslandExtras.tsx` (removed 2026-10-29).
|
||||
|
||||
```
|
||||
One-click SSH setup
|
||||
```
|
||||
|
||||
## What replaced it
|
||||
|
||||
Nothing. The Remote tile and section were removed rather than replaced; the
|
||||
existing Approvals cell already carries the verified local claim:
|
||||
|
||||
> Approve an agent's tool call from your Mac's notch, and the agent carries on.
|
||||
|
||||
## Restore checklist
|
||||
|
||||
1. FRE-320/FRE-205 have shipped the one-click install and removed the
|
||||
`~/.ssh/config` / `ssh-copy-id` friction described in FRE-320.
|
||||
2. The CTO has verified the shipping flow and the evidence is on FRE-320.
|
||||
3. A recording exists showing a remote agent joining the island.
|
||||
4. CMO restores the copy above, then owns the launch plan for it — with an
|
||||
owner and date per item — before anything goes back on the site.
|
||||
@@ -1,30 +0,0 @@
|
||||
# Why the remote claim is parked
|
||||
|
||||
The site was selling a setup path a buyer does not get.
|
||||
|
||||
FRE-320 measured today's actual path (2026-10-29): the one-click flow is
|
||||
app-UI-only, needs a hand-edited `~/.ssh/config` for any host not already in ssh
|
||||
config or on the tailnet, needs a one-time `ssh-copy-id` because the app runs ssh
|
||||
with `BatchMode=yes`, and has no manual fallback for minting the per-host
|
||||
credential. FRE-315 and FRE-322 §3 correction 4 marked the remote claim
|
||||
unverified and demo-gated. The CTO named it the largest launch risk because it
|
||||
was already in front of buyers.
|
||||
|
||||
So the `freno-dev` source now says only what the product can back today: local
|
||||
agents, the card, and the notch. That correction is committed locally and is
|
||||
**not deployed** — the live page still carries the remote sentences until the
|
||||
push in FRE-323 card `82d1e1b7` lands. The remote story is not deleted — it is
|
||||
parked here with the FRE-320/FRE-205 work so it can return the day the one-click
|
||||
install ships and is recorded.
|
||||
|
||||
## What has to be true before it returns
|
||||
|
||||
| Requirement | Owner | Date |
|
||||
|---|---|---|
|
||||
| One-click install ships without hand-edited `~/.ssh/config` or `ssh-copy-id` | FRE-320/FRE-205 engineering | When FRE-320/FRE-205 ships |
|
||||
| CTO verifies the shipping flow and records the evidence on FRE-320 | CTO | Same day, before any copy moves |
|
||||
| A recording shows a remote agent joining the island | Sponsor/CMO | Before restore |
|
||||
| Restore copy and rebuild the launch plan with an owner + date per item | CMO | Day the three rows above are true |
|
||||
|
||||
Nothing in this folder is publishable. `marketing/approved/` remains the only
|
||||
place a human publishes from.
|
||||
@@ -4,13 +4,13 @@ Every drafted asset draws its voice, claims, and constraints from this file. If
|
||||
artifact invents its own framing, fix the artifact or fix this file — never both
|
||||
silently diverging.
|
||||
|
||||
**Reconciled 2026-10-29**, and again **2026-11-03** for the FRE-997 count
|
||||
correction, against the CTO's verdicts ([FRE-315], [FRE-322], [FRE-354],
|
||||
[FRE-997]), the Chief of Staff's price decision (2026-10-29 04:01), the Plan B
|
||||
launch route ([FRE-346] → [FRE-203] `plan-b`), and `The-Nook` master (app floor is
|
||||
**14.2**, not 14.0 — `freno-dev` `881190d`, site merge `134f41e`). The previous
|
||||
version of this file named an upstream `the-nook/docs/marketing-strategy.md` that
|
||||
no longer exists; the verdicts below are the source of truth it was pointing at.
|
||||
**Reconciled 2026-10-29**, and again **2026-11-03** for the FRE-997 correction,
|
||||
against the CTO's verdicts ([FRE-315], [FRE-322], [FRE-354], [FRE-997]), the Chief
|
||||
of Staff's price decision (2026-10-29 04:01), the Plan B launch route ([FRE-346] →
|
||||
[FRE-203] `plan-b`), and `The-Nook` master (app floor is **14.2**, not 14.0 —
|
||||
`freno-dev` `881190d`, site merge `134f41e`). The previous version of this file
|
||||
named an upstream `the-nook/docs/marketing-strategy.md` that no longer exists; the
|
||||
verdicts below are the source of truth it was pointing at.
|
||||
|
||||
[FRE-315]: https://paperclip.freno.me/FRE/issues/FRE-315
|
||||
[FRE-322]: https://paperclip.freno.me/FRE/issues/FRE-322
|
||||
@@ -115,11 +115,16 @@ one thing forbidden: say "TestFlight beta" when the phone exists in a sentence a
|
||||
all, and never claim approvals-from-phone as a shipped capability without its own
|
||||
CTO verdict — no "(coming soon)" parentheticals either way.
|
||||
|
||||
Remote SSH is **held**, not approved: the one-click install has not
|
||||
been verified end to end ([FRE-315] §1) and the shipped path needs hand-edited
|
||||
`~/.ssh/config` and a one-time `ssh-copy-id` ([FRE-320]). Its copy stays parked in
|
||||
`marketing/drafts/remote-ssh/` until the CTO verifies the shipping flow **and** a
|
||||
recording shows a remote agent joining the notch.
|
||||
Remote SSH is **publishable**, and the site sells it again ([FRE-997]). The
|
||||
copy that was pulled on 2026-10-29 had been checked against claims it never made:
|
||||
hosts come from `~/.ssh/config` and Tailscale (`RemoteHostDiscovery`), the
|
||||
one-time `ssh-copy-id` is a prerequisite the row hands the user as a command
|
||||
(`RemoteHostStatus.fixCommand`), the per-host credential is minted and copied
|
||||
over (`RemoteMirror`), and a remote host mirrors this Mac's integrations
|
||||
(`RemoteMirrorReconcile`). Two things in the old copy were wrong and were fixed
|
||||
rather than restored: the "every hooked agent" overstatement, and a count that
|
||||
said thirteen against a registry of eighteen. Muse, Cline and Amp are
|
||||
`.unsupported` for remote in `AgentSpec.remote`, and the copy names them.
|
||||
|
||||
### 5. Humans hold every account
|
||||
|
||||
@@ -138,9 +143,9 @@ Each row names the evidence, not a feeling. Re-read the verdict before using a r
|
||||
| The hook and bridge are not a cloud service | **Verified code fact, held on disclosure** | [FRE-322] §2, wording corrected by [FRE-354] `(j)`. Held until Sentry is gated/removed and `/privacy` + `/faq` land. Never widen to "The Nook", never "nothing but your Mac". |
|
||||
| Fan, thermal and charge control | **No claim verdict yet — do not lead with it** | Backed by `helper/` (privileged helper, SMC, no kext), but no CTO sentence exists for it. Documentary, not launch. |
|
||||
| One-time purchase, no subscription | **Publishable fact, price not a launch asset** | $10, held at $10. No dated rise, no discount framing, no pricing copy. |
|
||||
| Agent coverage numbers ("eighteen agents", "18 agents") | **Publishable at 18** | [FRE-997]: the /agents table now lists every row of `AgentSpec.all` and `agents.test.ts` asserts equality with the 18-name registry in registry order, so the landing bullet, the marquee chip and the /agents copy say 18. No other number is publishable. |
|
||||
| Agent coverage numbers ("eighteen agents", "18 agents") | **Publishable at 18** | [FRE-997]: the /agents table now lists every row of `AgentSpec.all` and `agents.test.ts` asserts equality with the 18-name registry in registry order, so the landing bullet, the marquee chip and the /agents copy say 18. No other number is publishable, and `every` / `all` are still banned without naming the three local-only agents. |
|
||||
| macOS 14.2 or later | **Publishable fact** | The app cannot start below 14.2 (`freno-dev` `881190d`, site `134f41e`). "macOS 14+" is false and was fixed on the site. |
|
||||
| Remote agents over SSH, one click to install | **Held** | [FRE-315] §1, [FRE-320], [FRE-323]. Needs a verified one-click install plus a recording. |
|
||||
| Remote agents over SSH, one click to install | **Publishable, with the three local-only agents named** | [FRE-997]: `RemoteHostDiscovery` (ssh config + tailnet), `RemoteMirror` (hook push, credential, mirroring), `RemoteMirrorReconcile`, `RemoteHostStatus.fixCommand` (the one-time `ssh-copy-id` the row shows). `AgentSpec.remote` is `.unsupported` for Muse, Cline and Amp — the landing bullet and /agents paragraph name them; never say "every hooked agent". |
|
||||
| "Nothing leaves your Mac", "no telemetry", "local-only", "no account" | **False today — banned** | Sentry unconditional, licence check with hardware fingerprint ([FRE-322] §2.4). |
|
||||
| Approvals from the iPhone or Apple Watch | **No verdict — not a launch claim** | The companion is a TestFlight beta ([FRE-354]); the approval-from-phone claim has no CTO verdict and stays out of launch copy. |
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
For,
|
||||
Show,
|
||||
createEffect,
|
||||
createSignal,
|
||||
onCleanup,
|
||||
@@ -20,6 +21,7 @@ import { Campfire, type CampfireState } from "./Campfire";
|
||||
import {
|
||||
ApprovalCardMock,
|
||||
SessionRowMock,
|
||||
Glyph,
|
||||
CLAUDE,
|
||||
CODEX
|
||||
} from "./ModuleMocks";
|
||||
@@ -35,7 +37,7 @@ import {
|
||||
* Detailed feature breakdowns: alternating copy + live-demo sections.
|
||||
* Each demo stages the real interaction loop: attention flares, approval
|
||||
* resolves, fans throttle, the charge ceiling holds, meetings surface,
|
||||
* the camera wakes. Newer demos live in
|
||||
* the camera wakes, a remote host goes live. Newer demos live in
|
||||
* MoreDemos.tsx; app-anatomy pieces in ModuleMocks.tsx.
|
||||
*/
|
||||
|
||||
@@ -805,6 +807,112 @@ function CameraDemo() {
|
||||
);
|
||||
}
|
||||
|
||||
/* ── Demo 7: Settings ▸ Remote hosts — one click to Live ──────────── */
|
||||
|
||||
/** RemoteMirror's progress steps, in order. */
|
||||
const SETUP_STEPS = [
|
||||
"connecting",
|
||||
"pushing the hook binary",
|
||||
"configuring agents",
|
||||
"checking the host can reach this Mac"
|
||||
];
|
||||
|
||||
function RemoteHostDemo() {
|
||||
/* -1 not set up · 0…3 setting up · 4 live */
|
||||
const [step, setStep] = createSignal(-1);
|
||||
const [pressed, setPressed] = createSignal(false);
|
||||
useDemoLoop(11000, [
|
||||
[0, () => (setStep(-1), setPressed(false))],
|
||||
[1400, () => setPressed(true)],
|
||||
[1700, () => (setPressed(false), setStep(0))],
|
||||
[2700, () => setStep(1)],
|
||||
[3700, () => setStep(2)],
|
||||
[4700, () => setStep(3)],
|
||||
[5800, () => setStep(4)]
|
||||
]);
|
||||
const live = () => step() === 4;
|
||||
const working = () => step() >= 0 && step() < 4;
|
||||
const chip = () =>
|
||||
live()
|
||||
? { color: "rgb(48,209,88)", label: "Live" }
|
||||
: working()
|
||||
? { color: "rgba(255,255,255,0.55)", label: "Setting up" }
|
||||
: { color: "rgba(255,255,255,0.35)", label: "Not set up" };
|
||||
|
||||
return (
|
||||
<div class="relative mx-auto w-full max-w-[360px] space-y-3">
|
||||
{/* the host's card in Settings ▸ Agents ▸ Remote hosts */}
|
||||
<ModuleCard>
|
||||
<div class="space-y-2 text-left">
|
||||
<div class="flex items-center gap-2.5">
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-[13px] font-semibold text-white">gpu-rig</p>
|
||||
<p class="truncate text-[11px] text-white/45">tailnet peer</p>
|
||||
</div>
|
||||
<Show when={working()}>
|
||||
<span class="h-3 w-3 animate-spin rounded-full border-[1.5px] border-white/25 border-t-white/80" />
|
||||
</Show>
|
||||
<span class="flex items-center gap-1.5 text-[10.5px] font-medium text-white/85">
|
||||
<span
|
||||
class="h-[7px] w-[7px] rounded-full transition-colors duration-300"
|
||||
style={{ background: chip().color }}
|
||||
/>
|
||||
{chip().label}
|
||||
</span>
|
||||
<Show
|
||||
when={live()}
|
||||
fallback={
|
||||
<span
|
||||
class="rounded-md px-2.5 py-1 text-[11px] font-semibold text-white transition-all duration-200"
|
||||
style={{
|
||||
background: ACCENT,
|
||||
opacity: working() ? 0 : 1,
|
||||
transform: pressed() ? "scale(0.92)" : "scale(1)"
|
||||
}}
|
||||
>
|
||||
Set up
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Glyph name="refresh" size={11} class="text-white/50" />
|
||||
<span class="rounded-md bg-white/10 px-2 py-1 text-[11px] font-semibold text-[#ff6961]">
|
||||
Remove
|
||||
</span>
|
||||
</Show>
|
||||
</div>
|
||||
<p class="h-4 text-[11px] text-white/50">
|
||||
{working() ? SETUP_STEPS[step()] : " "}
|
||||
</p>
|
||||
</div>
|
||||
</ModuleCard>
|
||||
|
||||
{/* its agents arrive as ordinary rows, the machine named in the chip */}
|
||||
<div
|
||||
class="transition-all duration-500"
|
||||
style={{
|
||||
opacity: live() ? 1 : 0,
|
||||
transform: live() ? "translateY(0)" : "translateY(6px)"
|
||||
}}
|
||||
>
|
||||
<SessionRowMock
|
||||
title="trainer · fp16"
|
||||
prompt="benchmark fp16"
|
||||
state="Running python bench.py…"
|
||||
status="running"
|
||||
agent={CODEX}
|
||||
host="gpu-rig"
|
||||
origin="iTerm2"
|
||||
age="now"
|
||||
/>
|
||||
</div>
|
||||
<FakeCursor
|
||||
phase={pressed() ? "press" : step() === -1 ? "idle" : "live"}
|
||||
at={{ x: 88, y: 16 }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* ── Demo 8: campfire states ───────────────────────────────────────── */
|
||||
|
||||
function CampfireDemo() {
|
||||
@@ -868,6 +976,7 @@ export default function FeatureBreakdowns() {
|
||||
"Approval cards pin above the session list; questions answer inside their row",
|
||||
"Allow for session waves through the rest of that session's requests",
|
||||
"Answer from iPhone or Apple Watch too, now in TestFlight beta",
|
||||
"Works across local and remote sessions; Muse, Cline and Amp are this Mac only"
|
||||
]}
|
||||
reversed
|
||||
>
|
||||
@@ -887,6 +996,20 @@ export default function FeatureBreakdowns() {
|
||||
<TerminalReplyDemo />
|
||||
</BreakdownSection>
|
||||
|
||||
<BreakdownSection
|
||||
kicker="Remote"
|
||||
title="Any SSH box, one click away"
|
||||
body="Pick a host from your ssh config or your tailnet and click once — the nook-hook installs itself and its agents appear beside your local ones, indistinguishable."
|
||||
bullets={[
|
||||
"One click: connect, push the hook, configure agents, check it can reach this Mac",
|
||||
"Hosts mirror this Mac's installed integrations automatically",
|
||||
"Remote sessions are ordinary rows; the agent chip names the machine"
|
||||
]}
|
||||
reversed
|
||||
>
|
||||
<RemoteHostDemo />
|
||||
</BreakdownSection>
|
||||
|
||||
<BreakdownSection
|
||||
kicker="The campfire"
|
||||
title="A hearth that tells the truth"
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { For, createSignal, onCleanup, onMount, type JSX } from "solid-js";
|
||||
import { IslandPill, STATUS, CALM, CRITICAL, ModuleCard } from "./IslandMock";
|
||||
import { ApprovalCardMock, DieLine, CLAUDE } from "./ModuleMocks";
|
||||
import {
|
||||
ApprovalCardMock,
|
||||
SessionRowMock,
|
||||
DieLine,
|
||||
CLAUDE,
|
||||
CODEX
|
||||
} from "./ModuleMocks";
|
||||
|
||||
/**
|
||||
* Feature collage: five cells sold as the app's actual UI. The hero cell
|
||||
@@ -99,6 +105,35 @@ function FansPanelMock() {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote agents as the panel draws them: ordinary session rows whose agent
|
||||
* chip names the machine ("Claude Code · build-box").
|
||||
*/
|
||||
function RemoteMock() {
|
||||
return (
|
||||
<div class="space-y-1.5">
|
||||
<SessionRowMock
|
||||
title="api · main"
|
||||
prompt="fix the flaky test"
|
||||
state="Running pytest -x…"
|
||||
status="running"
|
||||
agent={CLAUDE}
|
||||
host="build-box"
|
||||
age="4m"
|
||||
/>
|
||||
<SessionRowMock
|
||||
title="trainer · fp16"
|
||||
prompt="benchmark fp16"
|
||||
state="Done · replied 1m ago"
|
||||
status="ready"
|
||||
agent={CODEX}
|
||||
host="gpu-rig"
|
||||
age="12m"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* Deterministic usage wander for the traces. */
|
||||
const wander = (n: number, k: number) =>
|
||||
0.42 + 0.22 * Math.sin(n * 0.45 + k * 1.7) + 0.12 * Math.sin(n * 1.3 + k);
|
||||
@@ -221,6 +256,19 @@ export default function FeatureCollage() {
|
||||
</InkStage>
|
||||
</CollageCell>
|
||||
|
||||
{/* Remote agents */}
|
||||
<CollageCell
|
||||
kicker="Remote"
|
||||
title="Agents on other machines"
|
||||
body="Run agents on Linux boxes and SSH servers — one click installs the nook-hook, and their sessions join the same island."
|
||||
>
|
||||
<InkStage class="w-full">
|
||||
<div class="w-full px-3 text-left">
|
||||
<RemoteMock />
|
||||
</div>
|
||||
</InkStage>
|
||||
</CollageCell>
|
||||
|
||||
{/* Gauges */}
|
||||
<CollageCell
|
||||
kicker="Your Mac"
|
||||
|
||||
@@ -740,6 +740,7 @@ const CHIPS_B = [
|
||||
"tmux · iTerm2 · kitty",
|
||||
"Markdown replies",
|
||||
"Tailscale hosts",
|
||||
"One-click SSH setup",
|
||||
"iPhone widgets",
|
||||
"Watch-face widgets",
|
||||
"Screenshots to clipboard",
|
||||
|
||||
@@ -25,6 +25,9 @@ const SOURCE = readFileSync(join(import.meta.dir, "agents.tsx"), "utf8");
|
||||
/** User-facing copy only: comments are not copy and may name the old wording. */
|
||||
const COPY = SOURCE.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "");
|
||||
|
||||
/** JSX wraps prose across lines; match a sentence, not its indentation. */
|
||||
const PROSE = COPY.replace(/\s+/g, " ");
|
||||
|
||||
/** AgentSpec.all display names, in registry order (18 rows). @ The-Nook f4258034, 2026-10-29 */
|
||||
const REGISTRY = [
|
||||
"Claude Code",
|
||||
@@ -67,3 +70,21 @@ describe("Nook /agents roster", () => {
|
||||
expect(COPY).not.toContain("all thirteen");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Nook /agents remote coverage", () => {
|
||||
it("says agents can run on remote machines over SSH", () => {
|
||||
expect(PROSE).toContain("remote machines");
|
||||
expect(PROSE).toContain("remote sessions report into the same grid");
|
||||
});
|
||||
|
||||
it("names the three agents that stay on this Mac", () => {
|
||||
expect(PROSE).toContain("Muse, Cline and Amp run on this Mac only");
|
||||
});
|
||||
|
||||
it("discloses the local-only rows where they are listed", () => {
|
||||
const localOnly = [
|
||||
...SOURCE.matchAll(/This Mac only — remote hosts skip it/g)
|
||||
];
|
||||
expect(localOnly.length).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -209,7 +209,8 @@ const AGENTS: AgentEntry[] = [
|
||||
},
|
||||
{
|
||||
name: "Cline",
|
||||
wiring: "One hook file per event in its hooks directory",
|
||||
wiring:
|
||||
"One hook file per event in its hooks directory. This Mac only — remote hosts skip it",
|
||||
events: [
|
||||
"SessionStart",
|
||||
"SessionEnd",
|
||||
@@ -334,7 +335,8 @@ const AGENTS: AgentEntry[] = [
|
||||
},
|
||||
{
|
||||
name: "Amp",
|
||||
wiring: "In-process extension package",
|
||||
wiring:
|
||||
"In-process extension package. This Mac only — remote hosts skip it",
|
||||
events: [
|
||||
"SessionStart",
|
||||
"SessionEnd",
|
||||
@@ -387,7 +389,9 @@ export default function NookAgentsPage() {
|
||||
Each agent reports its own set of events. "Allow / Deny from a card"
|
||||
means the agent hands the decision to The Nook while it waits;
|
||||
"Observe" means every event still appears live, but the agent keeps
|
||||
its own prompting.
|
||||
its own prompting. Agents can run on this Mac or on remote machines
|
||||
over SSH — remote sessions report into the same grid. Muse, Cline and
|
||||
Amp run on this Mac only.
|
||||
</p>
|
||||
|
||||
<div class="grid gap-4 lg:grid-cols-2">
|
||||
|
||||
@@ -1,41 +1,27 @@
|
||||
/**
|
||||
* Regression test for the removed remote / SSH claims and the removed agent
|
||||
* count (FRE-323, FRE-359 verdict B).
|
||||
* Guard for the remote / SSH story on the Nook site, and for the two phrasings
|
||||
* that must not come back with it (FRE-323 → FRE-997).
|
||||
*
|
||||
* FRE-323 took the remote story off the site because it was unverified and
|
||||
* demo-gated (FRE-315, FRE-320, FRE-322 §3 correction 4): the "one click
|
||||
* installs the nook-hook" flow is app-UI-only, needs a hand-edited
|
||||
* `~/.ssh/config` for any host not already in ssh config or on the tailnet,
|
||||
* needs a one-time `ssh-copy-id` because the app runs ssh with
|
||||
* `BatchMode=yes`, and has no manual fallback for minting the per-host
|
||||
* credential. FRE-359 verdict B removed the coverage count for the same
|
||||
* reason: the /agents table lists 13 of the registry's 18 rows.
|
||||
* FRE-323 removed the remote copy because it had been checked against claims
|
||||
* the copy never made: the app does list hosts from `~/.ssh/config` and
|
||||
* Tailscale, a one-time `ssh-copy-id` is a normal prerequisite the row explains,
|
||||
* and the per-host credential is minted and copied automatically
|
||||
* (`RemoteHostDiscovery`, `RemoteHostStatus.fixCommand`, `RemoteMirror`). Two
|
||||
* things in the old copy were genuinely wrong, and this file guards those
|
||||
* instead of the copy as a whole:
|
||||
*
|
||||
* FRE-997 corrects the second half without touching the first. The count was
|
||||
* stale, not unverifiable: `AgentSpec.all` holds eighteen, /agents now lists
|
||||
* all eighteen and `agents.test.ts` asserts that roster as an equality again.
|
||||
* So the two completeness phrasings FRE-359 banned ("every coding agent",
|
||||
* "Supported agents") no longer rest on a 13-of-18 gap — the ban existed
|
||||
* because the page claimed the set and listed two thirds of it. They are out
|
||||
* of `BANNED_COVERAGE` on this branch for that reason alone; the stale counts
|
||||
* stay banned, and every FRE-323 remote sentence stays banned, because this
|
||||
* branch still carries the FRE-323 site state.
|
||||
* - "Works across every hooked agent, local or remote" overstated it. Muse,
|
||||
* Cline and Amp are `.unsupported` for remote in `AgentSpec.remote`, so the
|
||||
* landing bullet and the /agents paragraph name them.
|
||||
* - The count said thirteen while `AgentSpec.all` holds eighteen. The fix was
|
||||
* to say eighteen, not to drop the count.
|
||||
*
|
||||
* The copy was not thrown away — it is parked in
|
||||
* `marketing/drafts/remote-ssh/` with the restore gate, and this test asserts
|
||||
* it is still there so nobody deletes the parked story as stale. It goes back
|
||||
* on the site only when the one-click install ships and is recorded.
|
||||
* So the removal guards from `d44133f` are gone: a removed sentence is exactly
|
||||
* what this task restores. What stays is the pair of bans, because either one
|
||||
* coming back would repeat the original defect.
|
||||
*
|
||||
* STATIC SOURCE AUDIT, the same pattern as `agents.test.ts` and
|
||||
* `downloads.test.ts` (SolidJS does not render under `bun:test`).
|
||||
*
|
||||
* Deliberate exceptions, not audited here — both were weighed with the Chief
|
||||
* of Staff at the go/no-go and left in place on purpose:
|
||||
* - `src/routes/nook/agents.tsx` Muse row: "This Mac only — remote hosts skip
|
||||
* it" is a limitation disclosure, not a capability claim.
|
||||
* - `src/components/nook/IslandExtras.tsx` "Tailscale hosts" chip: adjacent to
|
||||
* the removed remote story, but tailnet host lookup does exist. Raised on
|
||||
* FRE-323 rather than widened into this correction.
|
||||
*/
|
||||
import { describe, it, expect } from "bun:test";
|
||||
import { readFileSync } from "node:fs";
|
||||
@@ -43,7 +29,7 @@ import { join } from "node:path";
|
||||
|
||||
const ROOT = join(import.meta.dir, "../../..");
|
||||
|
||||
/** Shipped page and component source. Tests and drafts may quote the old copy. */
|
||||
/** Shipped page and component source. */
|
||||
const AUDITED = [
|
||||
"src/routes/nook/index.tsx",
|
||||
"src/routes/nook/agents.tsx",
|
||||
@@ -53,27 +39,15 @@ const AUDITED = [
|
||||
"src/components/nook/IslandExtras.tsx"
|
||||
];
|
||||
|
||||
/** Every sentence FRE-323 ordered off the site. */
|
||||
/** The overstatements the restored copy must never carry again. */
|
||||
const BANNED_REMOTE_CLAIMS = [
|
||||
"Agents on other machines",
|
||||
"one click installs the nook-hook",
|
||||
"Any SSH box",
|
||||
"Works across every hooked agent, local or remote",
|
||||
"Remote sessions are ordinary rows",
|
||||
"One-click SSH setup",
|
||||
"remote machines over SSH",
|
||||
"this Mac or remote machines"
|
||||
];
|
||||
|
||||
/** FRE-359 verdict B: no stale count, in any form.
|
||||
*
|
||||
* "every coding agent" and "Supported agents" left this list with FRE-997:
|
||||
* the page now lists all eighteen registry rows and `agents.test.ts` asserts
|
||||
* that equality, so the completeness claim is backed rather than incomplete. */
|
||||
const BANNED_COVERAGE = [
|
||||
// a universal claim: three registry agents cannot mirror over SSH
|
||||
"every hooked agent, local or remote",
|
||||
"Works across every hooked agent",
|
||||
// the stale count this task replaced
|
||||
"13 agents",
|
||||
"all thirteen",
|
||||
"thirteen coding agents",
|
||||
"13 agents"
|
||||
"thirteen coding agents"
|
||||
];
|
||||
|
||||
/** User-facing copy only: comments are not copy and may name the old wording. */
|
||||
@@ -83,9 +57,46 @@ function copyOf(file: string): string {
|
||||
.replace(/^\s*\/\/.*$/gm, "");
|
||||
}
|
||||
|
||||
describe("remote claims stay off the site (FRE-323)", () => {
|
||||
describe("the restored remote copy is on the site (FRE-997)", () => {
|
||||
it("the landing collage sells agents on other machines", () => {
|
||||
const copy = copyOf("src/components/nook/FeatureCollage.tsx");
|
||||
for (const phrase of [
|
||||
"Agents on other machines",
|
||||
"one click installs the nook-hook",
|
||||
"Remote"
|
||||
]) {
|
||||
expect(copy).toContain(phrase);
|
||||
}
|
||||
});
|
||||
|
||||
it("the landing breakdown keeps the SSH section and its demo", () => {
|
||||
const copy = copyOf("src/components/nook/FeatureBreakdowns.tsx");
|
||||
for (const phrase of [
|
||||
"Any SSH box, one click away",
|
||||
"Pick a host from your ssh config or your tailnet and click once",
|
||||
"Remote sessions are ordinary rows",
|
||||
"RemoteHostDemo"
|
||||
]) {
|
||||
expect(copy).toContain(phrase);
|
||||
}
|
||||
});
|
||||
|
||||
it("the chip is back", () => {
|
||||
expect(copyOf("src/components/nook/IslandExtras.tsx")).toContain(
|
||||
"One-click SSH setup"
|
||||
);
|
||||
});
|
||||
|
||||
it("/agents says remote sessions report into the same grid", () => {
|
||||
const copy = copyOf("src/routes/nook/agents.tsx");
|
||||
expect(copy).toContain("remote machines");
|
||||
expect(copy).toContain("remote sessions report into the same grid");
|
||||
});
|
||||
});
|
||||
|
||||
describe("the two original overstatements stay off the site (FRE-323, FRE-359)", () => {
|
||||
for (const file of AUDITED) {
|
||||
it(`${file} does not restore a removed remote sentence`, () => {
|
||||
it(`${file} claims no universal remote coverage and no stale count`, () => {
|
||||
const copy = copyOf(file);
|
||||
for (const phrase of BANNED_REMOTE_CLAIMS) {
|
||||
expect(copy.toLowerCase()).not.toContain(phrase.toLowerCase());
|
||||
@@ -93,34 +104,9 @@ describe("remote claims stay off the site (FRE-323)", () => {
|
||||
});
|
||||
}
|
||||
|
||||
it("/agents no longer offers remote sessions", () => {
|
||||
const copy = copyOf("src/routes/nook/agents.tsx");
|
||||
expect(copy).not.toContain("remote sessions report into the same grid");
|
||||
expect(copy).not.toContain("on this Mac or on remote");
|
||||
});
|
||||
|
||||
it("the parked copy still exists, so restoring the story is a revert and not a rewrite", () => {
|
||||
const parked = readFileSync(join(ROOT, "marketing/drafts/remote-ssh/copy.md"), "utf8");
|
||||
for (const phrase of ["Agents on other machines", "Any SSH box, one click away"]) {
|
||||
expect(parked).toContain(phrase);
|
||||
}
|
||||
expect(readFileSync(join(ROOT, "marketing/drafts/remote-ssh/rationale.md"), "utf8")).toContain(
|
||||
"FRE-320"
|
||||
it("the landing names the agents that stay local", () => {
|
||||
expect(copyOf("src/components/nook/FeatureBreakdowns.tsx")).toContain(
|
||||
"Muse, Cline and Amp are this Mac only"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("the agent count is corrected, not dropped (FRE-997)", () => {
|
||||
for (const file of AUDITED) {
|
||||
it(`${file} states no stale count`, () => {
|
||||
const copy = copyOf(file);
|
||||
for (const phrase of BANNED_COVERAGE) {
|
||||
expect(copy.toLowerCase()).not.toContain(phrase.toLowerCase());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
it("/agents states the registry's size", () => {
|
||||
expect(copyOf("src/routes/nook/agents.tsx")).toContain("Supported agents");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user