Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions .lane/reports/fix-recording-exporters-honor-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Lane 1 — Recording exporters honor `--help`

## Item

- [unreviewed-by-opus] Recording exporters (teleprompter / rehearsal / cockpit / queue) ignore --help and write arti

## Branch

`fix/recording-exporters-honor-help` (off `origin/main` @ `497d690`)

## Root cause

The four recording-batch operator scripts in `scripts/` never tested
`process.argv` for `--help` / `-h` and never routed their `--output=` /
`--html=` flags through the shared operator CLI safety helpers. Calling any
of them with `--help` ran the full batch export and silently rewrote the
recording-batch artifact in `prospects/` — the same hazard the prior
`#135` and `#99` fixes closed for the growth/ops exporters and the
retired broad-service writers.

| Script | Default artifact |
|---|---|
| `scripts/export-recording-cockpit.mjs` | `prospects/recording-cockpit.html` |
| `scripts/export-recording-queue.mjs` | `prospects/recording-queue.md` |
| `scripts/export-recording-rehearsal-check.mjs` | `prospects/recording-rehearsal-check.{md,html}` |
| `scripts/export-recording-teleprompter.mjs` | `prospects/recording-teleprompter.html` |

## Fix

All four scripts now follow the established `handleHelp` /
`resolveOutputPath` pattern that the prior recording-batch refactor
peers use:

1. Import `handleHelp` and `resolveOutputPath` from
`./lib/operator-cli.mjs`.
2. Call `handleHelp(args, "Usage: ...")` immediately after parsing
`process.argv`, before any work.
3. Resolve every operator-supplied `--output=` / `--html=` value through
`resolveOutputPath(..., { fallback })` so paths that escape the
service repository are refused, mirroring the safety contract used
by `export-market-proof-run.mjs`, `export-internal-dashboard.mjs`,
etc.

## Tests

- Extended
`scripts/test-active-operator-surfaces.mjs`'s `remainingHelpSurface`
list with the four recording-batch scripts. The shared loop already
enforces:
- exit 0 for both `--help` and `-h`,
- `Usage:` printed on stdout,
- no rewrite of any tracked, runtime, or retired artifact.
- Ran `node scripts/test-active-operator-surfaces.mjs` end-to-end —
passes (`Active operator surface checks passed.`).
- Manual probes (run in `/tmp/test-recording-help`, removed after):
- `node scripts/export-recording-queue.mjs --help` → exits 0, prints
`Usage: node scripts/export-recording-queue.mjs [--limit=5] [--output=prospects/recording-queue.md]`,
no `prospects/` created.
- `node scripts/export-recording-cockpit.mjs -h` → exits 0, prints
usage, no artifact.
- `node scripts/export-recording-teleprompter.mjs --help` → exits 0,
prints usage, no artifact.
- `node scripts/export-recording-rehearsal-check.mjs --help` → exits
0, prints usage, no artifact.
- `--output=/tmp/escape-*.md` → refused with
`Refusing --output=...: the path escapes the repository`, exit 1,
no file written outside the service root for all four scripts.

## Files

- `scripts/export-recording-cockpit.mjs` — add `handleHelp` +
`resolveOutputPath`.
- `scripts/export-recording-queue.mjs` — same.
- `scripts/export-recording-rehearsal-check.mjs` — same; routes both
`--output` and `--html` through `resolveOutputPath`.
- `scripts/export-recording-teleprompter.mjs` — same.
- `scripts/test-active-operator-surfaces.mjs` — register the four
scripts in `remainingHelpSurface` so the shared `--help` / `-h`
contract stays enforced.
10 changes: 7 additions & 3 deletions scripts/export-recording-cockpit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ import { execFileSync } from "node:child_process";
import { checkProspectReadiness, prospectWarningWeight } from "./lib/prospect-readiness.mjs";
import { sendChannelGuidance } from "./lib/send-channel-guidance.mjs";
import { routedContactPlan } from "./lib/contact-route.mjs";
import { handleHelp, resolveOutputPath } from "./lib/operator-cli.mjs";
import { listOutboundProspectFolders } from "./lib/outbound-prospects.mjs";

const limitArg = process.argv.find((arg) => arg.startsWith("--limit="));
const args = process.argv.slice(2);
handleHelp(args, `Usage: node scripts/export-recording-cockpit.mjs [--limit=5] [--output=prospects/recording-cockpit.html]`);
const limitArg = args.find((arg) => arg.startsWith("--limit="));
const limit = limitArg ? Number(limitArg.split("=")[1]) : 5;
const outputArg = process.argv.find((arg) => arg.startsWith("--output="));
const outputPath = outputArg ? outputArg.split("=")[1] : "prospects/recording-cockpit.html";
const outputArg = args.find((arg) => arg.startsWith("--output="));
const outputRel = outputArg ? outputArg.split("=")[1] : "prospects/recording-cockpit.html";
const outputPath = resolveOutputPath(outputRel, { fallback: "prospects/recording-cockpit.html" });

function listFolders(root) {
return listOutboundProspectFolders(root);
Expand Down
10 changes: 7 additions & 3 deletions scripts/export-recording-queue.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
#!/usr/bin/env node
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { handleHelp, resolveOutputPath } from "./lib/operator-cli.mjs";
import { listOutboundProspectFolders } from "./lib/outbound-prospects.mjs";
import { checkProspectReadiness, prospectWarningWeight } from "./lib/prospect-readiness.mjs";

const limitArg = process.argv.find((arg) => arg.startsWith("--limit="));
const args = process.argv.slice(2);
handleHelp(args, `Usage: node scripts/export-recording-queue.mjs [--limit=5] [--output=prospects/recording-queue.md]`);
const limitArg = args.find((arg) => arg.startsWith("--limit="));
const limit = limitArg ? Number(limitArg.split("=")[1]) : 5;
const outputArg = process.argv.find((arg) => arg.startsWith("--output="));
const outputPath = outputArg ? outputArg.split("=")[1] : "prospects/recording-queue.md";
const outputArg = args.find((arg) => arg.startsWith("--output="));
const outputRel = outputArg ? outputArg.split("=")[1] : "prospects/recording-queue.md";
const outputPath = resolveOutputPath(outputRel, { fallback: "prospects/recording-queue.md" });

function listFolders(root) {
return listOutboundProspectFolders(root);
Expand Down
7 changes: 5 additions & 2 deletions scripts/export-recording-rehearsal-check.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ import { checkProspectReadiness, prospectWarningWeight } from "./lib/prospect-re
import { sendChannelGuidance } from "./lib/send-channel-guidance.mjs";
import { routedContactPlan } from "./lib/contact-route.mjs";
import { localIsoDate } from "./date-utils.mjs";
import { handleHelp, resolveOutputPath } from "./lib/operator-cli.mjs";
import { listOutboundProspectFolders } from "./lib/outbound-prospects.mjs";

const args = process.argv.slice(2);
handleHelp(args, `Usage: node scripts/export-recording-rehearsal-check.mjs [--limit=5] [--output=prospects/recording-rehearsal-check.md] [--html=prospects/recording-rehearsal-check.html] [--include-smoke]`);
const limitArg = args.find((arg) => arg.startsWith("--limit="));
const outputArg = args.find((arg) => arg.startsWith("--output="));
const htmlArg = args.find((arg) => arg.startsWith("--html="));
const includeSmoke = args.includes("--include-smoke");
const limit = limitArg ? Number(limitArg.split("=")[1]) : 5;
const outputPath = outputArg ? outputArg.split("=")[1] : "prospects/recording-rehearsal-check.md";
const htmlPath = htmlArg ? htmlArg.split("=")[1] : "prospects/recording-rehearsal-check.html";
const outputRel = outputArg ? outputArg.split("=")[1] : "prospects/recording-rehearsal-check.md";
const outputPath = resolveOutputPath(outputRel, { fallback: "prospects/recording-rehearsal-check.md" });
const htmlPath = resolveOutputPath(htmlArg ? htmlArg.split("=")[1] : "prospects/recording-rehearsal-check.html", { flag: "--html", fallback: "prospects/recording-rehearsal-check.html" });
const today = localIsoDate();

function read(path) {
Expand Down
10 changes: 7 additions & 3 deletions scripts/export-recording-teleprompter.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import { dirname, join, relative } from "node:path";
import { checkProspectReadiness, prospectWarningWeight } from "./lib/prospect-readiness.mjs";
import { sendChannelGuidance } from "./lib/send-channel-guidance.mjs";
import { routedContactPlan } from "./lib/contact-route.mjs";
import { handleHelp, resolveOutputPath } from "./lib/operator-cli.mjs";
import { listOutboundProspectFolders } from "./lib/outbound-prospects.mjs";

const limitArg = process.argv.find((arg) => arg.startsWith("--limit="));
const args = process.argv.slice(2);
handleHelp(args, `Usage: node scripts/export-recording-teleprompter.mjs [--limit=5] [--output=prospects/recording-teleprompter.html]`);
const limitArg = args.find((arg) => arg.startsWith("--limit="));
const limit = limitArg ? Number(limitArg.split("=")[1]) : 5;
const outputArg = process.argv.find((arg) => arg.startsWith("--output="));
const outputPath = outputArg ? outputArg.split("=")[1] : "prospects/recording-teleprompter.html";
const outputArg = args.find((arg) => arg.startsWith("--output="));
const outputRel = outputArg ? outputArg.split("=")[1] : "prospects/recording-teleprompter.html";
const outputPath = resolveOutputPath(outputRel, { fallback: "prospects/recording-teleprompter.html" });

function listFolders(root) {
return listOutboundProspectFolders(root);
Expand Down
6 changes: 5 additions & 1 deletion scripts/test-active-operator-surfaces.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ try {
"export-owned-handoff-loom-cockpit.mjs",
"export-owned-product-case-studies.mjs",
"export-owned-product-workflow-proofs.mjs",
"export-owned-startup-proof-capture.mjs"
"export-owned-startup-proof-capture.mjs",
"export-recording-cockpit.mjs",
"export-recording-queue.mjs",
"export-recording-rehearsal-check.mjs",
"export-recording-teleprompter.mjs"
]
// The growth/ops exporters that overwrite tracked ACTIVE_OPERATOR_ARTIFACTS
// must also honor --help/-h: the same exit 0 + usage contract, and they
Expand Down