diff --git a/src/__tests__/cli-events.test.ts b/src/__tests__/cli-events.test.ts index dce0c17..b81dc44 100644 --- a/src/__tests__/cli-events.test.ts +++ b/src/__tests__/cli-events.test.ts @@ -1,6 +1,6 @@ -// Phase 125 Plan 02 Task 1 — CLI command-invocation event tests. +// CLI command-invocation event tests. // -// CONTEXT.md §5: every real command emits `cli_command_invoked` on exit +// Contract: every real command emits `cli_command_invoked` on exit // with `{ command, subcommand, exit_code, duration_ms, cli_version, // node_version, platform }`; meta commands (`help`, `--help`, `--version`) // are excluded; first-ever invocation emits `cli_first_run` once. Errors diff --git a/src/__tests__/errors-manifest.test.ts b/src/__tests__/errors-manifest.test.ts index 7ae43c7..f6fa5c0 100644 --- a/src/__tests__/errors-manifest.test.ts +++ b/src/__tests__/errors-manifest.test.ts @@ -1,4 +1,4 @@ -// Phase 123 Plan 10 — CLI error manifest drift test. +// CLI error manifest drift test. // // Asserts that every AppError subclass exported from `src/errors/index.ts` // matches its corresponding entry in `src/errors/manifest.json` (which is @@ -33,7 +33,7 @@ const manifest = manifestJson as { // CLI-specific AppError subclasses that are intentionally NOT in the monorepo // manifest (they exist only in the CLI surface area). // -// `CliError` is the Phase 108 legacy base — a concrete class that extends +// `CliError` is the legacy base — a concrete class that extends // `AppError` but has no severity-tier meaning of its own (default sev3). It // stays in the CLI surface for `instanceof CliError` back-compat (sandbox-listen // picker/binary, index.ts main()) and is NOT part of the monorepo taxonomy. @@ -96,7 +96,7 @@ describe('CLI error manifest drift', () => { it('the 15 canonical manifest classes are all present (sanity check on the mirrored JSON)', () => { // This test fails if the monorepo manifest is ever sync-fetched in a - // reduced state. Keep it locked to the Phase 123 Plan 02 shape. + // reduced state. Keep it locked to this shape. const names = Object.keys(manifest.classes).sort(); expect(names).toEqual( [ diff --git a/src/__tests__/flush-exit.test.ts b/src/__tests__/flush-exit.test.ts index ffc1a98..e26d698 100644 --- a/src/__tests__/flush-exit.test.ts +++ b/src/__tests__/flush-exit.test.ts @@ -1,6 +1,6 @@ -// Phase 125 Plan 02 Task 1 — flushAndExit covers BOTH Sentry + PostHog. +// flushAndExit covers BOTH Sentry + PostHog. // -// CONTEXT.md §125-02 must_haves: process exit awaits `posthog.shutdown(2000)` +// Contract: process exit awaits `posthog.shutdown(2000)` // + `Sentry.flush(2000)` in parallel via Promise.allSettled — neither // vendor's failure can block the other. import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; diff --git a/src/__tests__/manifest-drift.test.ts b/src/__tests__/manifest-drift.test.ts index 6259319..a1e0be8 100644 --- a/src/__tests__/manifest-drift.test.ts +++ b/src/__tests__/manifest-drift.test.ts @@ -1,4 +1,4 @@ -// Phase 125 Plan 01 Task 2 — CLI-side event-manifest drift test. +// CLI-side event-manifest drift test. // // Guards the CLI-LOCAL side of the manifest mirror: // 1. The JSON manifest parses with `version: '1'` + 24 unique event names. diff --git a/src/__tests__/posthog-alias.test.ts b/src/__tests__/posthog-alias.test.ts index d983ee1..a146ff0 100644 --- a/src/__tests__/posthog-alias.test.ts +++ b/src/__tests__/posthog-alias.test.ts @@ -1,19 +1,19 @@ -// Phase 125 Plan 02 Task 1 — PostHog alias-on-login contract tests. +// PostHog alias-on-login contract tests. // -// CONTEXT.md §3 + RESEARCH §Pattern 3 + §Pitfall 1: +// Alias contract: // The CLI captures pre-login events under a machine-id (`distinctId`). // On the FIRST successful login per (machine, user) pair, we call // `posthog.alias({ distinctId: workosSub, alias: machineId })` so PostHog // stitches the anonymous machine-scoped events onto the user profile that // the app + marketing already write. // -// Direction matters (RESEARCH §Pitfall 1): `distinctId` is the canonical user +// Direction matters: `distinctId` is the canonical user // id (workosSub); `alias` is the side identifier we want to merge in // (machineId). Reversing the args — `{ distinctId: machineId, alias: sub }` — // would silently merge the user profile into the machine, breaking app↔CLI // stitching for every event from then on. // -// Per-pair persistence (CONTEXT.md §3): `posthogAliasedUsers: [...]` in +// Per-pair persistence : `posthogAliasedUsers: [...]` in // config.json prevents repeated alias calls on every login; a different // workosSub on the same machine still fires alias once for that pair. import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; diff --git a/src/__tests__/posthog-init.test.ts b/src/__tests__/posthog-init.test.ts index a7ab098..f86b0fd 100644 --- a/src/__tests__/posthog-init.test.ts +++ b/src/__tests__/posthog-init.test.ts @@ -1,4 +1,4 @@ -// Phase 125 Plan 02 Task 1 — PostHog lazy-init contract tests. +// PostHog lazy-init contract tests. // // Mirrors `sentry-init.test.ts` shape verbatim so failures land with the same // debug ergonomics. Asserts: @@ -8,7 +8,7 @@ // 3. `initPostHogLazy()` is a NO-OP when no token is baked (dev build). // 4. `initPostHogLazy()` returns a real PostHog client when both telemetry // is enabled AND a token is present, with `flushAt: 1, flushInterval: 0` -// (short-lived-process pattern from RESEARCH §Pattern 1). +// (short-lived-process pattern). // 5. Subsequent calls reuse the same instance — no re-init. import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; diff --git a/src/__tests__/sandbox-listen/version-check.test.ts b/src/__tests__/sandbox-listen/version-check.test.ts index c61594d..c3c6a90 100644 --- a/src/__tests__/sandbox-listen/version-check.test.ts +++ b/src/__tests__/sandbox-listen/version-check.test.ts @@ -29,7 +29,7 @@ describe('checkForNewerCli', () => { expect(logSpy).toHaveBeenCalledOnce(); const msg = logSpy.mock.calls[0][0] as string; - // Locked prefix per CONTEXT.md §CLI Flow Step 2 + // Locked prefix per the version-check contract expect(msg.startsWith('A newer version of hookmyapp is available')).toBe(true); expect(msg).toContain(pkg.version); expect(msg).toContain('99.0.0'); diff --git a/src/__tests__/sentry-init.test.ts b/src/__tests__/sentry-init.test.ts index 4add69c..41b0f61 100644 --- a/src/__tests__/sentry-init.test.ts +++ b/src/__tests__/sentry-init.test.ts @@ -1,4 +1,4 @@ -// Phase 123 Plan 10 — Sentry lazy-init tests. +// Sentry lazy-init tests. // // Contract: // @@ -18,7 +18,7 @@ // // 5. `shouldCaptureToSentry()` filters backend-response wrappers (any error // with a non-undefined statusCode → false) — implements the -// single-capture-per-error rule from CONTEXT.md. +// single-capture-per-error rule. import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { initSentryLazy, diff --git a/src/__tests__/telemetry-consent.test.ts b/src/__tests__/telemetry-consent.test.ts index 58620b7..e736049 100644 --- a/src/__tests__/telemetry-consent.test.ts +++ b/src/__tests__/telemetry-consent.test.ts @@ -1,4 +1,4 @@ -// Phase 123 Plan 10 — telemetry consent tests. +// telemetry consent tests. // // Exercises the three-layer override chain defined in // `src/observability/telemetry.ts`: @@ -217,8 +217,8 @@ afterEach(() => { } }); -// Phase 125 Plan 02: HOOKMYAPP_TELEMETRY=off is the SINGLE kill-switch that -// covers both Sentry AND PostHog (CONTEXT.md §4 — "single CLI opt-out"). +// an earlier release: HOOKMYAPP_TELEMETRY=off is the SINGLE kill-switch that +// covers both Sentry AND PostHog . // This block lives here (not in posthog-init.test.ts) so the contract // "isTelemetryEnabled is the one shared gate" is documented next to the gate // itself. diff --git a/src/__tests__/workspace-list.spec.ts b/src/__tests__/workspace-list.spec.ts index 49aed27..1ba06eb 100644 --- a/src/__tests__/workspace-list.spec.ts +++ b/src/__tests__/workspace-list.spec.ts @@ -30,7 +30,7 @@ const mockConsoleLog = vi.spyOn(console, 'log').mockImplementation(() => {}); const CONFIG_PATH = path.join(TMP_HOME, '.hookmyapp', 'config.json'); -// Phase 117: every workspace id fixture is a ws_ publicId. +// an earlier release: every workspace id fixture is a ws_ publicId. // AIT-182: fixtures simulate an OLDER backend that still sends // workosOrganizationId — the CLI must scrub it at the output boundary. const fakeWorkspaces = [ diff --git a/src/__tests__/workspace.test.ts b/src/__tests__/workspace.test.ts index 2592abf..5869ca9 100644 --- a/src/__tests__/workspace.test.ts +++ b/src/__tests__/workspace.test.ts @@ -33,7 +33,7 @@ const mockedApiClient = vi.mocked(apiClient); const mockedOutput = vi.mocked(output); const mockedRescopeWorkspaceToken = vi.mocked(rescopeWorkspaceToken); -// Phase 117 — every id fixture below is a publicId (ws_/ch_/ssn_/mem_/inv_ prefix, 8-char +// every id fixture below is a publicId (ws_/ch_/ssn_/mem_/inv_ prefix, 8-char // alphanumeric body). Raw UUIDs are rejected with a typed ValidationError at every // external flag/header/body surface; see workspace.ts resolveWorkspace + _helpers.ts. // AIT-182 — the workspaces wire no longer carries workosOrganizationId. @@ -93,8 +93,8 @@ describe('resolveWorkspace', () => { await expect(resolveWorkspace('Nonexistent')).rejects.toThrow('not found'); }); - // Phase 117 hard cutover — raw UUID input is not an accepted shape. - it('throws typed CliError when given a raw UUID (Phase 117 hard cutover)', async () => { + // an earlier release hard cutover — raw UUID input is not an accepted shape. + it('throws typed CliError when given a raw UUID (an earlier release hard cutover)', async () => { mockedApiClient.mockResolvedValue(fakeWorkspaces); const { CliError } = await import('../output/error.js'); await expect(resolveWorkspace('aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa')).rejects.toThrow(CliError); @@ -172,7 +172,7 @@ describe('writeWorkspaceConfig config-file merge', () => { expect(ws.activeWorkspaceSlug).toBe('Another Workspace'); }); - // Phase 117 — readWorkspaceConfig silently drops a stale UUID activeWorkspaceId + // readWorkspaceConfig silently drops a stale UUID activeWorkspaceId // (pre-0.5.0 install artifact). Prevents the UUID from leaking back out to the // backend which now 400s on every UUID input. it('silently drops stale UUID activeWorkspaceId on read (pre-0.5.0 config)', async () => { @@ -529,7 +529,7 @@ describe('workspace commands', () => { } }); - // Phase 117 hard cutover — negative regression. + // an earlier release hard cutover — negative regression. it('workspace use rejects raw UUID with typed ValidationError', async () => { mockedApiClient.mockResolvedValue(fakeWorkspaces); @@ -596,7 +596,7 @@ describe('resolveInviteByIdOrEmail', () => { expect(invite).toEqual(fakeMembersResponse.invites[0]); }); - it('finds invite by inv_ publicId (Phase 117 — not raw UUID)', async () => { + it('finds invite by inv_ publicId (an earlier release — not raw UUID)', async () => { const responseWithPublicIdInvite = { members: [], invites: [ diff --git a/src/analytics/events.manifest.json b/src/analytics/events.manifest.json index 54daf84..4c81f1d 100644 --- a/src/analytics/events.manifest.json +++ b/src/analytics/events.manifest.json @@ -1,7 +1,7 @@ { "version": "1", "generatedAt": "2026-04-30T00:00:00Z", - "description": "CLI-side typed PostHog event registry (scoped subset). Declares only events the CLI binary actually emits — the 8 cli_* events. The cross-repo drift test in /Users/ordvir/COD/hookmyapp/packages/observability/src/analytics/__tests__/manifest-drift.spec.ts asserts every event here also appears in the monorepo's events.manifest.json with the same property set. Baseline properties (site, environment, workspace_id, days_since_signup) are implicit on every event and injected by super-properties.ts — not listed per-event below.", + "description": "CLI-side typed analytics event registry (scoped subset). Declares only events the CLI binary actually emits — the 8 cli_* events. A drift test asserts every event here also appears in the upstream registry with the same property set. Baseline properties (site, environment, workspace_id, days_since_signup) are implicit on every event and injected by super-properties.ts — not listed per-event below.", "events": [ { "name": "cli_first_run", diff --git a/src/analytics/events.ts b/src/analytics/events.ts index b5dd31b..fbf6206 100644 --- a/src/analytics/events.ts +++ b/src/analytics/events.ts @@ -1,7 +1,7 @@ // CLI-side typed event registry — scoped subset of the monorepo's full // registry at packages/observability/src/analytics/events.ts. // -// History: from Phase 125 through Phase 130 this file was a byte-for-byte +// History: from an earlier release through an earlier release this file was a byte-for-byte // mirror of the monorepo registry. That coupled CLI to every monorepo event // addition (web, web-api, demo, marketing) even though CLI emits only the // 7 `cli_*` events. With monorepo's surface growing (frontend + backend + diff --git a/src/api/client.ts b/src/api/client.ts index 2949caf..040cb1d 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -190,8 +190,8 @@ export async function rescopeWorkspaceToken(workspaceId: string): Promise // consistent across commands. Keep this in sync with the error-hierarchy // contract in output/error.ts (exit codes: 2 / 3 / 4 / 5 / 6). // -// Return type is `CliError` — a Phase 108 alias that is identical at runtime -// to `AppError` under Phase 123 Plan 10. Existing callers see no change. +// Return type is `CliError` — a an earlier release alias that is identical at runtime +// to `AppError` under an earlier release. Existing callers see no change. export async function mapApiError(res: Response): Promise { // eslint-disable-next-line @typescript-eslint/no-explicit-any const body: any = await res.json().catch(() => ({ message: res.statusText })); @@ -227,7 +227,7 @@ export async function mapApiError(res: Response): Promise { return new PermissionError(cfg.activeWorkspaceSlug ?? ''); } if (res.status === 409) return new ConflictError(msg, code ?? 'CONFLICT'); - // Phase 122 — bootstrap-code exchange error mapping. + // bootstrap-code exchange error mapping. // 404 and 410 collapse to the same user-facing message (oracle-attack // defense — brute-force guessers can't distinguish "unknown code" from // "already spent code"). ApiError.exitCode defaults to 1; we override @@ -263,8 +263,8 @@ export async function mapApiError(res: Response): Promise { ); } if (res.status === 429) { - // Phase 123 Plan 10 — use the new RateLimitError class (sev3, httpStatus - // 429). Exit code remains 6 so the Phase 108 exit-code contract for 429 + // use the new RateLimitError class (sev3, httpStatus + // 429). Exit code remains 6 so the an earlier release exit-code contract for 429 // is preserved (historically this flowed through ConflictError → exit 6). // The body.code 'RATE_LIMITED' matches the backend's // UserIdThrottlerGuard structured 429 body. @@ -319,7 +319,7 @@ export async function apiClient( throw new AuthError('Not logged in. Run: hookmyapp login'); } - // Phase 123 Plan 10 — set Sentry user tag on every authenticated API call. + // set Sentry user tag on every authenticated API call. // Idempotent + lazy (no-op when telemetry off or Sentry not initialized). // Fire-and-forget so the API call's latency isn't gated on dynamic import. void (async () => { @@ -401,7 +401,7 @@ export async function apiClient( } } -// --- Phase 126 sandbox bind-code contract --- +// --- sandbox bind-code contract --- // // Mirrors backend/src/sandbox/bind-code.controller.ts (Plan 03 Wave 2 locked // contract: `GET /sandbox/bind-code` returns the caller's available bind code diff --git a/src/api/version-headers.ts b/src/api/version-headers.ts index c4764b5..2c17be7 100644 --- a/src/api/version-headers.ts +++ b/src/api/version-headers.ts @@ -38,7 +38,7 @@ const OS = process.platform; * Stainless-style headers used by the OpenAI and Anthropic SDKs * (`x-stainless-package-version` + `x-stainless-lang/runtime/arch/os`). * - * Contract per docs/superpowers/specs/2026-05-06-cli-and-skill-version-enforcement-design.md: + * The contract: * - User-Agent: hookmyapp-cli/ (node/; ; ) * - X-HookMyApp-CLI-Version: (always) * - X-HookMyApp-Lang: node (always) diff --git a/src/auth/__tests__/login.test.ts b/src/auth/__tests__/login.test.ts index 14f80f1..8f9cf9d 100644 --- a/src/auth/__tests__/login.test.ts +++ b/src/auth/__tests__/login.test.ts @@ -165,7 +165,7 @@ describe('post-login wizard', () => { logSpy.mockRestore(); }); - it('--next sandbox --phone matches existing → delegates to listen (Phase 126 bind-code model)', async () => { + it('--next sandbox --phone matches existing → delegates to listen', async () => { apiClientMock // workspaces fetch .mockResolvedValueOnce([ @@ -176,7 +176,7 @@ describe('post-login wizard', () => { }, ]) // sandbox sessions fetch — the session already exists (bound previously - // via `hookmyapp sandbox start`). Phase 126 no longer POSTs to + // via `hookmyapp sandbox start`). The current flow no longer POSTs to // /sandbox/sessions; binding is inbound-message driven. .mockResolvedValueOnce([ { @@ -209,7 +209,7 @@ describe('post-login wizard', () => { const [path, init] = call; if (path === '/sandbox/sessions' && init?.method === 'POST') { throw new Error( - 'Regression: wizard POSTed to /sandbox/sessions which was deleted in Phase 126 Plan 03', + 'Regression: wizard POSTed to /sandbox/sessions which was deleted', ); } } diff --git a/src/auth/login.ts b/src/auth/login.ts index c3b5421..ae5807b 100644 --- a/src/auth/login.ts +++ b/src/auth/login.ts @@ -16,7 +16,7 @@ import { posthogAliasAndIdentify } from '../observability/posthog.js'; import { parseSandboxSessions, type WhatsAppSandboxSession } from '../api/sandbox-session.js'; import { maybeInstallClaudeMcp } from '../commands/mcp.js'; -// --- Phase 122 bootstrap-code exchange DTO --- +// --- bootstrap-code exchange DTO --- // Mirrors backend/src/auth/bootstrap/dto/exchange-bootstrap.dto.ts (Wave 1 // locked contract). The CLI does not import from the backend — the DTO is // re-declared here verbatim so drift is caught by integration tests. @@ -80,7 +80,7 @@ async function pollForTokens(opts: { refreshToken: data.refresh_token, expiresAt: Math.floor(Date.now() / 1000) + 900, }); - // Phase 125 — alias machineId → workosSub once per (machine, user) and + // alias machineId → workosSub once per (machine, user) and // emit cli_logged_in. Fail-open: a posthog hiccup must never block the // login UX. Pass email + name so the PostHog Person profile shows the // human identity for CLI events. @@ -238,7 +238,7 @@ export async function runWizard(opts: WizardOpts = {}): Promise { * Sandbox sub-flow. Invoked by the wizard when the user passes * `hookmyapp login --next sandbox` (or `--phone ` without `--next`). * - * Phase 126 bind-code model — session creation is phone-initiated (user + * The bind-code model — session creation is phone-initiated (user * sends a bind code from their WhatsApp into the env's sandbox number — * staging IL or prod US, per env-profiles.ts), NOT CLI-flag-initiated. * The wizard therefore no longer offers a @@ -248,7 +248,7 @@ export async function runWizard(opts: WizardOpts = {}): Promise { * 0 sessions → runSandboxStart (prints bind code + QR + polls; on poll success, * runSandboxListenFlow starts automatically because `--listen` is true). * 1 session → direct listen (matches the "single active session" fast-path - * from before Phase 126 — preserved so repeated logins don't + * from an older CLI — preserved so repeated logins don't * force the user through bind flow again). * N sessions → picker (no "+ Create new" — bind flow handles that; the * picker is purely for choosing which ALREADY-bound phone to @@ -286,7 +286,7 @@ export async function runSandboxFlow( ); // --phone is authoritative: match against existing active sessions only. - // Phase 126 — we do NOT POST /sandbox/sessions here (endpoint deleted); + // we do NOT POST /sandbox/sessions here (endpoint deleted); // binding is phone-initiated via an inbound WhatsApp message matching // the user's bind code, which the dedicated `sandbox start` command // drives. @@ -375,7 +375,7 @@ async function runChannelsConnectFlow(): Promise { } /** - * Phase 122: bootstrap-code exchange branch. Invoked when the user (or their + * Bootstrap-code exchange branch. Invoked when the user (or their * AI) runs `hookmyapp login --code hma_boot_<32>`. Bypasses the WorkOS device * flow entirely — zero browser interaction, zero polling — then re-enters * runWizard so the rest of the CLI (active workspace, --phone/--next hooks, @@ -448,7 +448,7 @@ export async function runBootstrapCodeExchange( }); maybeInstallClaudeMcp(); - // Phase 125 — alias machineId → workosSub once per (machine, user) and + // alias machineId → workosSub once per (machine, user) and // emit cli_logged_in. workspace publicId is already on disk above so // baseline workspace_id resolves. Fail-open: posthog hiccup ≠ blocked login. // Pass email so the PostHog Person profile shows the human identity for CLI @@ -741,7 +741,7 @@ export function loginCommand(program: Command): void { return; } - // Phase 122 — bootstrap-code branch. MUST run BEFORE the wizard + // bootstrap-code branch. MUST run BEFORE the wizard // fast-path and BEFORE device-flow initiation so --code --wizard is // flagged as a programming error (mutually exclusive). if (opts.code) { diff --git a/src/commands/__tests__/wizard.test.ts b/src/commands/__tests__/wizard.test.ts index a790f83..9f42db3 100644 --- a/src/commands/__tests__/wizard.test.ts +++ b/src/commands/__tests__/wizard.test.ts @@ -1,9 +1,9 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; -// Phase 126 — wizard sandbox sub-flow, post-bind-code rework. +// wizard sandbox sub-flow, post-bind-code rework. // // The wizard (runSandboxFlow in src/auth/login.ts) was wired against the -// legacy click-path `POST /sandbox/sessions`, which Phase 126 Plan 03 deleted +// legacy click-path `POST /sandbox/sessions`, which an earlier release deleted // from the backend. The rewrite delegates to the bind-code-driven // runSandboxStart for the no-existing-session case and keeps the // "listen on the session you already bound" shortcut for repeat logins. @@ -19,7 +19,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; // `hookmyapp sandbox start` (NO POST to /sandbox/sessions). // 6. No test case POSTs to `/sandbox/sessions` — that endpoint is gone. // -// Guardrail (RESEARCH.md anti-pattern): the wizard must NOT spawn any +// Guardrail: the wizard must NOT spawn any // subprocess. runSandboxStart imports are stubbed here so the real command // body isn't invoked during the test. @@ -74,7 +74,7 @@ beforeEach(async () => { runSandboxFlow = (mod as any).runSandboxFlow; }); -describe('wizard sandbox sub-flow — Phase 126 bind-code rework', () => { +describe('wizard sandbox sub-flow — an earlier release bind-code rework', () => { it('0 sessions → delegates to runSandboxStart with listen:true (no POST to /sandbox/sessions)', async () => { apiClientMock.mockResolvedValueOnce([]); await runSandboxFlow(); @@ -87,7 +87,7 @@ describe('wizard sandbox sub-flow — Phase 126 bind-code rework', () => { const [path, init] = call; if (path === '/sandbox/sessions' && init?.method === 'POST') { throw new Error( - 'Regression: wizard POSTed to /sandbox/sessions which was deleted in Phase 126 Plan 03', + 'Regression: wizard POSTed to /sandbox/sessions which was deleted', ); } } diff --git a/src/commands/_helpers.ts b/src/commands/_helpers.ts index 42b4565..e2bf597 100644 --- a/src/commands/_helpers.ts +++ b/src/commands/_helpers.ts @@ -52,12 +52,12 @@ export async function getDefaultWorkspaceId(): Promise { const { program } = await import('../index.js'); const flag = program.opts().workspace as string | undefined; if (flag) { - // Phase 117: raw UUID shape is never a valid --workspace value. Short- + // A raw UUID shape is never a valid --workspace value. Short- // circuit with a typed ValidationError before the /workspaces round-trip // so scripts see exit 2 with a clear remediation hint. if (isLikelyUuid(flag)) { throw new ValidationError( - `--workspace "${flag}" is a raw UUID. Phase 117 CLI requires a publicId (ws_<8-char>) or workspace name. Re-run: hookmyapp workspace list`, + `--workspace "${flag}" is a raw UUID. The CLI requires a publicId (ws_<8-char>) or workspace name. Re-run: hookmyapp workspace list`, ); } const workspaces = await listWorkspacesOrEmpty(); @@ -186,17 +186,17 @@ export async function resolveChannelRefOrDefault( } /** - * Phase 125 Plan 02 — wrap a command body with `cli_command_invoked` / - * `cli_error_shown` instrumentation (CONTEXT.md §5). + * wrap a command body with `cli_command_invoked` / + * `cli_error_shown` instrumentation . * * Contract: * - On success → emits `cli_command_invoked` with `{ command, subcommand, * exit_code: 0, duration_ms, ... }`. * - On thrown CliError → emits `cli_command_invoked` with the mapped - * exit_code (Phase 108 table) AND `cli_error_shown` with the error code, + * exit_code (the exit-code table) AND `cli_error_shown` with the error code, * then re-throws so the outer main() catch in src/index.ts handles * formatting + flushAndExit normally. - * - help / --help / -h / --version / -v → no events (CONTEXT.md §5 + * - help / --help / -h / --version / -v → no events (the analytics contract * "skips help, --version, and pure-meta commands"); the wrapper just * runs `fn` and returns its result. * diff --git a/src/commands/channels-listen/index.ts b/src/commands/channels-listen/index.ts index d4770ce..61a99f7 100644 --- a/src/commands/channels-listen/index.ts +++ b/src/commands/channels-listen/index.ts @@ -1,7 +1,5 @@ // `hookmyapp channels listen` — real-channel mirror of `sandbox listen`. // -// Spec: docs/superpowers/specs/2026-05-15-cli-channel-listen-design.md -// Plan: docs/superpowers/plans/2026-05-15-cli-channel-listen-cli.md // // Shares the binary, proxy-server, summarizer, version-check, and // graceful-shutdown machinery with sandbox-listen — what's different is the diff --git a/src/commands/channels.ts b/src/commands/channels.ts index 6551c84..6d46374 100644 --- a/src/commands/channels.ts +++ b/src/commands/channels.ts @@ -113,7 +113,7 @@ function throwNoMatch(needle: string, channels: Channel[]): never { `Run: ${cliCommandPrefix()} channels list`, 'CHANNEL_NOT_FOUND', ); - // Preserve the Phase 108 exit-code contract for resolve-no-match (2). + // Preserve the an earlier release exit-code contract for resolve-no-match (2). // NotFoundError defaults to 1 but this caller always treated it as // ValidationError-class (bad argv pointing at a non-existent channel). err.exitCode = 2; diff --git a/src/commands/config.ts b/src/commands/config.ts index 447db61..45ec7fa 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -195,7 +195,7 @@ EXAMPLES: ? 'config.json' : 'default'; - // Phase 123 Plan 10 — telemetry state appears in `config show` so users + // telemetry state appears in `config show` so users // have a single authoritative surface to see what's reported. const persistedTelemetry = getPersistedTelemetry(); const telemetryActive = isTelemetryEnabled() ? 'on' : 'off'; diff --git a/src/commands/sandbox-listen/binary.ts b/src/commands/sandbox-listen/binary.ts index 061b2e7..e67eaa1 100644 --- a/src/commands/sandbox-listen/binary.ts +++ b/src/commands/sandbox-listen/binary.ts @@ -3,8 +3,8 @@ // Why we self-own the binary rather than depend on the npm `cloudflared` package: // - Release cadence is ours, not Cloudflare's. Pin in source (CLOUDFLARED_VERSION). // - We verify SHA-256 against a hardcoded manifest (Cloudflare does not publish -// per-asset checksums — see 107-RESEARCH.md §Pitfall 5). -// - Platform asset formats differ (RESEARCH §Pitfall 4): +// per-asset checksums). +// - Platform asset formats differ : // macOS → .tgz archive (extract `cloudflared` member) // Linux → standalone binary // Windows → standalone .exe diff --git a/src/commands/sandbox-listen/index.ts b/src/commands/sandbox-listen/index.ts index 6756549..4e711cd 100644 --- a/src/commands/sandbox-listen/index.ts +++ b/src/commands/sandbox-listen/index.ts @@ -1,8 +1,8 @@ // `hookmyapp sandbox listen` — Commander subcommand wiring the 11-step flow -// from 107-CONTEXT.md §CLI Flow. Composes the Plan 09a modules (binary, -// proxy-server, summarizer, version-check) with Plan 09b's picker + lifecycle. +// Composes the sandbox-listen modules (binary, +// proxy-server, summarizer, version-check) with the picker + lifecycle modules. // -// Exit code contract (CONTEXT.md §CLI Flow Exit codes): +// Exit code contract : // 0 clean // 1 not authenticated // 2 no active sessions / --phone|--session mismatch @@ -194,7 +194,7 @@ export async function runSandboxListenFlow( // Step 9 — tunnel heartbeat loop (pings backend /tunnel/heartbeat every 30s // so the backend doesn't reap the session; separate from PostHog heartbeat - // which is pure analytics every 5 minutes — CONTEXT.md §5 decision). + // which is pure analytics every 5 minutes). const hb = startHeartbeat({ sessionId: session.id, workspaceId: effectiveWorkspaceId, @@ -216,7 +216,7 @@ export async function runSandboxListenFlow( // // This Promise is the ONLY thing keeping the parent process alive after // setup — without it, src/index.ts:219 (`await flushAndExit(0)`) would - // run immediately and the listen command would exit. RESEARCH §Pitfall 6 + // run immediately and the listen command would exit. // is now subsumed by case (c): the cloudflared-exit handler triggers the // same gracefulShutdown that stops the PostHog heartbeat. let shuttingDown = false; @@ -310,7 +310,7 @@ export function registerListenCommand(sandbox: Command, program: Command): void .action(async (identifier: string | undefined, opts: ListenOpts) => { const human = !program.opts().json && !opts.json; - // Step 0 — validate --session flag shape. Phase 117: must be a + // Step 0 — validate --session flag shape. an earlier release: must be a // ssn_<8-char> publicId; raw UUIDs are rejected here rather than at // the backend so CI scripts see exit 2 with the typed CliError code // instead of a 400 round-trip. diff --git a/src/commands/sandbox-listen/lifecycle.ts b/src/commands/sandbox-listen/lifecycle.ts index 61cc4ab..5bbe9ca 100644 --- a/src/commands/sandbox-listen/lifecycle.ts +++ b/src/commands/sandbox-listen/lifecycle.ts @@ -1,5 +1,5 @@ // Cloudflared subprocess + heartbeat + graceful-shutdown machinery for -// `hookmyapp sandbox listen`. See RESEARCH §Pattern 5 (lines 470-506). +// `hookmyapp sandbox listen`. // // CRITICAL pitfalls addressed here: // §Pitfall 1 — NO `--url` flag on spawn. Token-mode tunnels are "remotely @@ -198,7 +198,7 @@ export interface GracefulShutdownArgs { } /** - * Shutdown order (CONTEXT.md §CLI Flow Step 11): + * Shutdown order : * stopHeartbeat() → proxyClose() → callBackendStop() → child.kill('SIGTERM') * wait up to 5s for child exit, then SIGKILL. * diff --git a/src/commands/sandbox-listen/picker.ts b/src/commands/sandbox-listen/picker.ts index 2b3bf05..9a549b2 100644 --- a/src/commands/sandbox-listen/picker.ts +++ b/src/commands/sandbox-listen/picker.ts @@ -1,6 +1,6 @@ // Session picker for `hookmyapp sandbox listen`. // -// Contract (107-CONTEXT.md §CLI Flow Step 5): +// The picker contract: // 0 sessions → throw NO_ACTIVE_SESSIONS (exit 2) // 1 session → return silently (auto-select) // 2+ + no flag + human TTY → interactive @inquirer/prompts select diff --git a/src/commands/sandbox-listen/proxy-server.ts b/src/commands/sandbox-listen/proxy-server.ts index 6ed114c..b358798 100644 --- a/src/commands/sandbox-listen/proxy-server.ts +++ b/src/commands/sandbox-listen/proxy-server.ts @@ -3,7 +3,7 @@ // Pipeline: cloudflared (token-mode) → CF edge → PUT-configured ingress rule → // http://127.0.0.1: → forwards to dev's real localhost:. // -// CRITICAL: bind 127.0.0.1 ONLY (not 0.0.0.0). See RESEARCH §Pitfall 9 — +// CRITICAL: bind 127.0.0.1 ONLY (not 0.0.0.0). — // binding to 0.0.0.0 exposes the proxy to the LAN where scanners can race // the free-port assignment between `listen()` and the backend's // `PUT /configurations` call. diff --git a/src/commands/sandbox-listen/version-check.ts b/src/commands/sandbox-listen/version-check.ts index 8b9538d..57ca9d6 100644 --- a/src/commands/sandbox-listen/version-check.ts +++ b/src/commands/sandbox-listen/version-check.ts @@ -1,6 +1,6 @@ // Non-blocking version-nudge check for `hookmyapp sandbox listen`. // -// Contract locked by 107-CONTEXT.md §CLI Flow Step 2: +// The version-check contract: // - GET https://registry.npmjs.org/@gethookmyapp/cli/latest // - 2s hard timeout via AbortSignal.timeout(2000) // - If newer, print EXACTLY: @@ -33,7 +33,7 @@ export async function checkForNewerCli(): Promise { if (typeof latest !== 'string' || latest.length === 0) return; const current = await readCliVersion(); if (latest === current) return; - // Locked one-liner — Plan 09b smoke test asserts the exact prefix. + // Locked one-liner — a smoke test asserts the exact prefix. console.log( `A newer version of hookmyapp is available (${current} → ${latest}). Run npm update -g @gethookmyapp/cli`, ); diff --git a/src/commands/workspace.ts b/src/commands/workspace.ts index 77bf7ba..7e101cc 100644 --- a/src/commands/workspace.ts +++ b/src/commands/workspace.ts @@ -45,7 +45,7 @@ export function readWorkspaceConfig(): WorkspaceConfig { const perEnv = full.activeWorkspaceByEnv?.[resolveEnv()]; const candidateId = perEnv?.id ?? full.activeWorkspaceId; const candidateSlug = perEnv?.id ? perEnv.slug : full.activeWorkspaceSlug; - // Phase 117 hard cutover: activeWorkspaceId on disk MUST be a ws_ publicId. + // Hard cutover: activeWorkspaceId on disk MUST be a ws_ publicId. // A stale UUID from a pre-0.5.0 install is silently dropped so the next // caller falls through to the single-workspace auto-pick or the login // wizard's picker. No UUID value ever leaks back out to the backend. @@ -58,7 +58,7 @@ export function readWorkspaceConfig(): WorkspaceConfig { } export function writeWorkspaceConfig(config: WorkspaceConfig): void { - // Phase 117 hard cutover: refuse to persist a UUID-shaped activeWorkspaceId. + // Hard cutover: refuse to persist a UUID-shaped activeWorkspaceId. // Symmetric with the read-side drop in readWorkspaceConfig — invariant is // "no UUID ever reaches disk or escapes to the backend." if (config.activeWorkspaceId && !isValidPublicId(config.activeWorkspaceId, 'ws')) { @@ -83,14 +83,14 @@ export function writeWorkspaceConfig(config: WorkspaceConfig): void { } export async function resolveWorkspace(nameOrId: string, kind?: 'team' | 'customer'): Promise<{ id: string; name: string; role: string }> { - // Phase 117: raw UUID input is not an accepted shape. A `ws_` publicId or + // Raw UUID input is not an accepted shape. A `ws_` publicId or // a workspace name are the accepted identifier shapes — matching what the // backend surfaces over HTTP (AIT-182 removed the internal WorkOS org id // from the wire). If the caller passes a UUID, short-circuit with a typed // error instead of silently accepting it (would 400 at the backend). if (isLikelyUuid(nameOrId)) { throw new ValidationError( - `workspace identifier "${nameOrId}" is a raw UUID. Phase 117 CLI requires a publicId (ws_<8-char>) or workspace name. Re-run: hookmyapp workspace list`, + `workspace identifier "${nameOrId}" is a raw UUID. The CLI requires a publicId (ws_<8-char>) or workspace name. Re-run: hookmyapp workspace list`, ); } diff --git a/src/config/__tests__/env-profiles.test.ts b/src/config/__tests__/env-profiles.test.ts index ed484f0..da906a5 100644 --- a/src/config/__tests__/env-profiles.test.ts +++ b/src/config/__tests__/env-profiles.test.ts @@ -209,7 +209,7 @@ describe('env-profiles: getEffectiveSandboxProxyUrl', () => { }); }); - // E4 — staging targets dedicated staging sandbox-proxy (Phase 120) + // E4 — staging targets dedicated staging sandbox-proxy it('env=staging, no env var → returns https://staging-sandbox.hookmyapp.com', () => { withTempConfig(() => { setPersistedEnv('staging'); diff --git a/src/config/index.ts b/src/config/index.ts index 051a57d..387c7df 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,4 +1,4 @@ -// Phase 125 Plan 02 — PostHog-related config slice. +// PostHog-related config slice. // // `~/.hookmyapp/config.json` is the shared on-disk store the CLI uses for // every persistent setting (workspace state, env profile, telemetry consent, @@ -12,7 +12,7 @@ // index.ts entry, sandbox-listen lifecycle) → `import {…} from // '../config/index.js'` reads cleanly and matches the plan's // list. -// 3. CONTEXT.md §3 + §16 require these keys to be persistent + shared +// 3. The telemetry contract requires these keys to be persistent + shared // across multiple call sites — a module dedicated to them prevents // accidental shape drift. // @@ -41,13 +41,13 @@ import { getConfigFile, safeWriteFileSync } from '../storage/path.js'; export interface PosthogConfigSlice { /** * Stable per-installation UUID generated lazily on the first PostHog need - * (CONTEXT.md §3 — anonymous distinct_id for pre-login captures). Persists + * . Persists * forever once written so multi-day installs stay attributable. */ posthogDistinctId?: string; /** * WorkOS subs that have been aliased to `posthogDistinctId` on THIS machine - * (CONTEXT.md §3 — once-per-(machine,user) alias semantics). Repeated + * alias semantics). Repeated * logins for an already-aliased sub skip the alias call; logins for a new * sub on the same machine still alias once for that pair. */ @@ -60,7 +60,7 @@ export interface PosthogConfigSlice { lastWorkosSub?: string; /** * ISO-8601 UTC date the workspace owner signed up. Used to compute - * `days_since_signup` baseline property (CONTEXT.md §16). Optional — + * `days_since_signup` baseline property . Optional — * `null` baseline value when missing. Future plan can populate this from * a backend call; today we leave it unset and accept the null. */ diff --git a/src/errors/base.ts b/src/errors/base.ts index c48918f..6f5731d 100644 --- a/src/errors/base.ts +++ b/src/errors/base.ts @@ -1,4 +1,4 @@ -// Phase 123 Plan 10 — AppError base class (mirrored from monorepo). +// AppError base class (mirrored from monorepo). // // Source of truth is `packages/observability/src/errors/base.ts` in the // hookmyapp monorepo. The CLI is a separate public repo (github.com/hookmyapp/cli) @@ -10,7 +10,7 @@ // Severity is a STATIC readonly property on each subclass. The class selection // IS the severity decision — no per-throw judgment. // -// Phase 108's CLI exit-code hierarchy (0–6) is preserved via `exitCodeFor()` +// an earlier release's CLI exit-code hierarchy (0–6) is preserved via `exitCodeFor()` // in `src/output/error.ts`. Class-to-exit-code mapping: // AuthError → 4 // PermissionError → 3 diff --git a/src/errors/index.ts b/src/errors/index.ts index b8193cc..50afb19 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -1,4 +1,4 @@ -// Phase 123 Plan 10 — single entry point for the CLI's AppError taxonomy. +// single entry point for the CLI's AppError taxonomy. // // This barrel re-exports classes from two sources: // @@ -9,9 +9,9 @@ // // - `../output/error.ts` — the concrete CLI AppError subclasses // (UserBlockingError, ValidationError, AuthError, …). They layer on top of -// `CliError` (Phase 108 legacy base) so that `instanceof CliError` -// invariants from Phase 108 code paths AND `instanceof AppError` -// invariants from Phase 123 code paths hold simultaneously. +// `CliError` (an earlier release legacy base) so that `instanceof CliError` +// invariants from legacy code paths AND `instanceof AppError` +// invariants from legacy code paths hold simultaneously. // // The drift test (`__tests__/errors-manifest.test.ts`) enumerates the // AppError subclasses in `../output/error.ts` and cross-checks each against diff --git a/src/index.ts b/src/index.ts index ef5ce08..2e00c4c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -68,7 +68,7 @@ program.addOption( ).hideHelp(), ); -// Phase 125 — capture the command + subcommand resolved by Commander into +// capture the command + subcommand resolved by Commander into // module-level state so main() can emit `cli_command_invoked` with the // correct names without each action handler having to wrap itself. Commander // passes `actionCommand` (the leaf) AND we walk parents to reconstruct the @@ -258,13 +258,13 @@ async function main(): Promise { // Same fail-open policy as the config migration. } - // Phase 123 Plan 10 — init Sentry early so top-level throws + unhandled + // init Sentry early so top-level throws + unhandled // rejections capture before we hit the exit boundary. The function is // idempotent + lazy: if telemetry is disabled (HOOKMYAPP_TELEMETRY=off // or `config set telemetry off`), Sentry is never loaded. await initSentryLazy(); - // Phase 125 Plan 02 — emit cli_first_run on the first-ever invocation per + // emit cli_first_run on the first-ever invocation per // machine. Idempotent: subsequent invocations short-circuit on the // persisted machine-id presence. Skipped silently when telemetry is off. // Wrapped: telemetry must NEVER block the CLI. ConfigWriteForbiddenError diff --git a/src/lib/publicId.ts b/src/lib/publicId.ts index 1289789..f984b5f 100644 --- a/src/lib/publicId.ts +++ b/src/lib/publicId.ts @@ -5,7 +5,7 @@ // (monorepo-internal workspace package). Keep the alphabet / length / // prefix list in sync manually on any future changes. // -// Phase 117 (prefixed public IDs) — the CLI only validates incoming flag +// an earlier release (prefixed public IDs) — the CLI only validates incoming flag // strings and treats server-returned publicId values as opaque identifiers // everywhere else. No generation happens on the CLI side. diff --git a/src/observability/jwt-light.ts b/src/observability/jwt-light.ts index b3653a6..a84a925 100644 --- a/src/observability/jwt-light.ts +++ b/src/observability/jwt-light.ts @@ -1,4 +1,4 @@ -// Phase 123 Plan 10 — minimal JWT `sub` decoder. +// minimal JWT `sub` decoder. // // We can't pull `jose` (or similar) into the CLI just for one field — the // token is already trusted (we issued it; the refresh flow validates diff --git a/src/observability/posthog.ts b/src/observability/posthog.ts index da3bd7e..c22df57 100644 --- a/src/observability/posthog.ts +++ b/src/observability/posthog.ts @@ -1,4 +1,4 @@ -// Phase 125 Plan 02 — CLI PostHog wiring. +// CLI PostHog wiring. // // Mirrors `sentry.ts` shape verbatim so observability stays one consistent // mental model. Key contracts: @@ -7,25 +7,25 @@ // BOTH `isTelemetryEnabled()` AND a token is baked. Telemetry-off users // pay zero cold-start cost. // -// 2. SHORT-LIVED PROCESS PATTERN (RESEARCH §Pattern 1). The CLI is short- +// 2. SHORT-LIVED PROCESS PATTERN. The CLI is short- // lived (most commands exit in <1s), so we configure `flushAt: 1` + // `flushInterval: 0`. Every capture is queued for immediate send; the // 2s `shutdown(2000)` drain at exit picks up the trailing in-flight // requests. Without this, posthog-node's default 20-event-or-10s // batching loses every CLI capture. // -// 3. SINGLE KILL-SWITCH (CONTEXT.md §4). `isTelemetryEnabled()` is the same +// 3. SINGLE KILL-SWITCH . `isTelemetryEnabled()` is the same // function `sentry.ts` already calls — `HOOKMYAPP_TELEMETRY=off` OR // `hookmyapp config set telemetry off` kills BOTH SDKs. No vendor- // specific flag. // -// 4. ALIAS DIRECTION (RESEARCH §Pitfall 1). `posthogAliasAndIdentify()` calls +// 4. ALIAS DIRECTION. `posthogAliasAndIdentify()` calls // `client.alias({ distinctId: workosSub, alias: machineId })` — distinctId // is the canonical user, alias is the side identifier we want stitched in. // Reversing the args silently merges the user profile into the machine // forever; we have a unit test pinning the direction. // -// 5. ONCE-PER-(MACHINE,USER) ALIAS (CONTEXT.md §3). The `posthogAliasedUsers` +// 5. ONCE-PER-(MACHINE,USER) ALIAS . The `posthogAliasedUsers` // array in config.json blocks repeated alias calls for the same sub on // the same machine; a different sub on the same machine still aliases // once for that pair (handles shared dev machines + identity switches). @@ -90,7 +90,7 @@ export async function initPostHogLazy(): Promise { try { const { PostHog } = await import('posthog-node'); const host = resolveHost(); - // Short-lived process pattern (RESEARCH §Pattern 1): + // Short-lived process pattern: // flushAt: 1 → queue size of 1 → every capture sends immediately // flushInterval: 0 → disable the periodic batcher // Combined with the 2s `shutdown(2000)` drain at exit (see flushAndExit @@ -259,7 +259,7 @@ export async function emitParseError(opts: { } /** - * Skip-list filter for `cli_command_invoked` (CONTEXT.md §5). Help / version + * Skip-list filter for `cli_command_invoked` . Help / version * / pure-meta commands are no-ops on the user's mental model — emitting * `cli_command_invoked` for them inflates volume + skews the funnel. * @@ -299,7 +299,7 @@ export async function maybeEmitFirstRun(): Promise { * Once-per-(machine, user) alias. Called from auth/login.ts after both the * device-flow and `--code` bootstrap paths persist credentials. * - * Direction (RESEARCH §Pitfall 1): + * Direction: * client.alias({ distinctId: workosSub, alias: machineId }) * * workosSub is the canonical user identity; machineId is the side @@ -341,7 +341,7 @@ export async function posthogAliasAndIdentify(opts: { writePosthogConfig({ lastWorkosSub: sub }); } - // Phase 125 follow-up — attach email + name to the PostHog person so the + // an earlier release follow-up — attach email + name to the PostHog person so the // Persons UI displays the user identity for CLI events without requiring // a frontend visit. Mirrors workspace-context.tsx's identify shape; $set // overwrites every login (email/name can drift). No-op when WorkOS didn't diff --git a/src/observability/sentry.ts b/src/observability/sentry.ts index 6bca8a1..5b0605d 100644 --- a/src/observability/sentry.ts +++ b/src/observability/sentry.ts @@ -1,6 +1,6 @@ -// Phase 123 Plan 10 — lazy Sentry init + flush-on-exit + setUser. +// lazy Sentry init + flush-on-exit + setUser. // -// Design rules (from 123-RESEARCH.md Pattern 7): +// Design rules: // // 1. LAZY INIT. Sentry is a ~50–80 KB module. The CLI is a short-lived // command-line tool invoked frequently (every sandbox listen tick, every @@ -16,7 +16,7 @@ // 2-second `Sentry.close()` then exits — the drain is best-effort (happy // path: 50 ms; worst case: 2 s). // -// 3. CAPTURE ONLY LOCAL CLI FAILURES. Per CONTEXT.md §single-capture-per-error: +// 3. CAPTURE ONLY LOCAL CLI FAILURES. Rule: // the CLI captures command crashes, pre-backend network failures (fetch // TypeError / DNS), and unhandled rejections. It does NOT re-capture HTTP // 5xx from the backend — the backend already has those events with full @@ -301,7 +301,7 @@ export async function captureError(err: unknown): Promise { * Replaces direct `process.exit()` at the top-level main() boundary + * unhandledRejection handler. * - * Phase 125 (CONTEXT.md §125-02 must_haves): both vendors are awaited in + * both vendors are awaited in * PARALLEL via Promise.allSettled — neither rejection blocks the other, * neither vendor's slow drain serializes behind the other. If neither SDK * is initialized this is essentially a no-op + immediate exit (zero added diff --git a/src/observability/telemetry.ts b/src/observability/telemetry.ts index 132b9da..efeae37 100644 --- a/src/observability/telemetry.ts +++ b/src/observability/telemetry.ts @@ -1,4 +1,4 @@ -// Phase 123 Plan 10 — CLI telemetry consent. +// CLI telemetry consent. // // Default: telemetry ON. Industry norm for product CLIs (npm, Next.js, Vercel, // Homebrew). Users already have an authenticated account with the product; diff --git a/src/output/error.ts b/src/output/error.ts index e59f6f9..bc9c8b1 100644 --- a/src/output/error.ts +++ b/src/output/error.ts @@ -1,12 +1,12 @@ -// Phase 123 Plan 10 — CLI error surface. +// CLI error surface. // -// This file previously defined a standalone `CliError` hierarchy (Phase 108). +// This file previously defined a standalone `CliError` hierarchy. // It now layers the AppError taxonomy from `src/errors/` UNDER the existing // `CliError` base class, preserving two invariants simultaneously: -// 1. `err instanceof CliError` — Phase 108 code paths (sandbox-listen/picker, +// 1. `err instanceof CliError` — legacy code paths (sandbox-listen/picker, // sandbox-listen/binary, index.ts main(), api-403-handler.spec.ts) keep // working. -// 2. `err instanceof AppError` — new Phase 123 code + Sentry `captureError` +// 2. `err instanceof AppError` — current code + Sentry `captureError` // path see typed AppError subclasses with severity tagging. // // `CliError` is a concrete subclass of `AppError` (sev3 default — a safe @@ -27,7 +27,7 @@ // Instances still carry `.userMessage`, `.code`, `.statusCode`, `.exitCode` so // the `outputError()` helper keeps working without caller changes. // -// Phase 108 exit-code contract: +// The exit-code contract: // AuthError → 4 // PermissionError → 3 // ValidationError → 2 @@ -45,7 +45,7 @@ export type { Severity, SentryLevel } from '../errors/base.js'; export { SEVERITY_TO_LEVEL, severityToLevel } from '../errors/base.js'; /** - * Phase 108 legacy base class. Now a concrete subclass of `AppError` so that: + * Legacy base class. Now a concrete subclass of `AppError` so that: * * 1. `instanceof CliError` guards continue to work (sandbox-listen picker / * binary / index.ts main() all use this). @@ -278,7 +278,7 @@ export class ClientOutdatedError extends CliError { } /** - * Map any thrown value to a CLI exit code. Phase 108's exit-code contract is + * Map any thrown value to a CLI exit code. The exit-code contract is * preserved via a lookup on the subclass identity: * AuthError → 4, PermissionError → 3, ValidationError → 2, ConflictError → 6, * RateLimitError → 6, NetworkError → 5, everything else → 1. @@ -294,7 +294,7 @@ export function exitCodeFor(err: unknown): number { if (err instanceof NetworkError) return 5; if (err instanceof CliError) { // Prefer the per-instance exitCode override (sandbox-listen picker/binary, - // Phase 122 bootstrap-code API errors set this explicitly). + // an earlier release bootstrap-code API errors set this explicitly). return err.exitCode ?? 1; } return 1;