From 426a7207bd8572cc9ef1ac02ec8c4392295de4c2 Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Thu, 14 May 2026 22:13:00 -0400 Subject: [PATCH 1/3] fix(save-ai-memory): TSC strict-mode errors in process-extract.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve 6 errors surfaced by `exactOptionalPropertyTypes: true` + `noUncheckedIndexedAccess: true`: - argv[++i] returns `string | undefined`; assigning to `string` field fails under exactOptionalPropertyTypes. Add nextArg() helper that guards undefined + exits with a clear "Missing value for " error, then assigns the validated string. - name[0] returns `string | undefined`; use name.charAt(0) (returns `string`) — already guarded by name.length === 0 check above. Behavior change is minimal and user-facing-friendly: previously, `bun process-extract.ts --ai-name` (missing value) would silently swallow the next flag as the name. Now it exits 1 with a clear error message naming the offending flag. Verified: `bunx tsc --noEmit` passes on the full project after fix; fails with TS2412 + TS2532 before fix. Co-Authored-By: Claude --- tools/save-ai-memory/process-extract.ts | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/tools/save-ai-memory/process-extract.ts b/tools/save-ai-memory/process-extract.ts index 50a7e93cc..d295b26d1 100644 --- a/tools/save-ai-memory/process-extract.ts +++ b/tools/save-ai-memory/process-extract.ts @@ -83,26 +83,35 @@ function parseArgs(argv: string[]): Args { commit: false, dryRun: false, }; - for (let i = 0; i < argv.length; i++) { + let i = 0; + function nextArg(name: string): string { + const v = argv[++i]; + if (v === undefined) { + console.error(`Missing value for ${name}`); + process.exit(1); + } + return v; + } + for (; i < argv.length; i++) { const a = argv[i]; switch (a) { case "--ai-name": - args.aiName = argv[++i]; + args.aiName = nextArg("--ai-name"); break; case "--platform": - args.platform = argv[++i] as Platform; + args.platform = nextArg("--platform") as Platform; break; case "--topic": - args.topic = argv[++i]; + args.topic = nextArg("--topic"); break; case "--conversation-id": - args.conversationId = argv[++i]; + args.conversationId = nextArg("--conversation-id"); break; case "--input": - args.input = argv[++i]; + args.input = nextArg("--input"); break; case "--output": - args.output = argv[++i]; + args.output = nextArg("--output"); break; case "--scrub-emails": args.scrubEmails = true; @@ -245,7 +254,7 @@ function generateOutputPath(args: Args, isoDate: string): string { function capitalizeName(name: string): string { if (name.length === 0) return name; - return name[0].toUpperCase() + name.slice(1); + return name.charAt(0).toUpperCase() + name.slice(1); } function buildArchive( From c86f9f4ea593c934259a3e7d515939b093bfa0b8 Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Thu, 14 May 2026 22:14:31 -0400 Subject: [PATCH 2/3] =?UTF-8?q?shard(tick):=200213Z=20=E2=80=94=20PR=20#33?= =?UTF-8?q?49=20opened=20(TSC=20fix=20on=20save-ai-memory=20canonical=20TS?= =?UTF-8?q?=20impl);=20auto-merge=20armed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- .../hygiene-history/ticks/2026/05/15/0213Z.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 docs/hygiene-history/ticks/2026/05/15/0213Z.md diff --git a/docs/hygiene-history/ticks/2026/05/15/0213Z.md b/docs/hygiene-history/ticks/2026/05/15/0213Z.md new file mode 100644 index 000000000..543b65f77 --- /dev/null +++ b/docs/hygiene-history/ticks/2026/05/15/0213Z.md @@ -0,0 +1,30 @@ +# Tick 0213Z — PR #3349 opened + auto-merge armed; TSC strict-mode fix landed on save-ai-memory canonical TS impl + +## Headline + +- PR [#3349](https://github.com/Lucent-Financial-Group/Zeta/pull/3349) OPENED + auto-merge armed. Resolves 6 `tsc` strict-mode errors in `tools/save-ai-memory/process-extract.ts` (the canonical TS impl that landed in [#3337](https://github.com/Lucent-Financial-Group/Zeta/pull/3337)). Currently `BLOCKED gate / 0 failed required / autoMerge armed / wait-ci`. +- Fix shape: `nextArg(name)` helper guards `string | undefined` from `argv[++i]`; `name.charAt(0)` replaces `name[0]` for `noUncheckedIndexedAccess`-clean access. One file, 17/8 line delta. +- Real footgun caught by strict mode: pre-fix, `bun process-extract.ts --ai-name` (missing value) would silently swallow the next flag as the value. Post-fix exits 1 with `Missing value for --ai-name`. + +## What I found mid-tick (substrate-honest disclosure) + +- Stale `.git/rebase-merge/` directory pointing at `lior/decompose-b0139-4` (May 14 20:36 mtime, ~1.5hrs old) sitting in the primary worktree's `.git/`. My HEAD was correctly on `fix/save-ai-memory-tsc-errors-otto-cli-2026-05-15`; the orphaned rebase state is orthogonal to my branch ref. Did NOT touch it per [`.claude/rules/claim-acquire-before-worktree-work.md`](../../../../../../.claude/rules/claim-acquire-before-worktree-work.md) worktree force-remove guard (analogous principle for peer-agent rebase state). +- Inline branch guard (`test "$(git branch --show-current)" = "" && git commit`) used per [`.claude/rules/zeta-expected-branch.md`](../../../../../../.claude/rules/zeta-expected-branch.md) field-test caveat — the env-var hook is defense-in-depth, not primary. +- Explicit `--head` passed to `gh pr create` per the same rule's companion defense. + +## Δ since 0051Z (last shard) + +| What | Before | After | +|---|---|---| +| Main HEAD | `e692e90` (#3330) | `343aef4` (#3338) — 5 PRs merged in the gap | +| Otto-CLI open PRs | 1 (#3332) | 1 (#3349) | +| `tsc --noEmit` clean? | NO (6 errors in save-ai-memory) | YES | +| save-ai-memory CLI arg-handling | silent on missing value | `Missing value for ` + exit 1 | + +## Cron sentinel + +`a2c54a1c` armed at session start (CronList was empty — fresh-session catch-43-defence fired). + +## Next + +Cron-driven. Next tick verifies #3349 merge state. If still BLOCKED, investigate threads. If MERGED, scan ready-to-grind backlog rows (3 work-assignment envelopes from 23:58Z still pending: B-0441, B-0170, B-0503; some may have expired at 01:58Z — check before claiming). From 82cb97cdc3ed00af0bc2f551ae0c375e9eb37574 Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Thu, 14 May 2026 22:27:16 -0400 Subject: [PATCH 3/3] fix(save-ai-memory): tighten arg-parsing per Copilot review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two P1 findings addressed: 1. **Flag-as-value detection** (PR thread on line 93): `nextArg` previously only rejected `undefined`, so `--ai-name --platform grok` would silently consume `--platform` as the name. Now rejects any next-token that starts with `--` and exits 1 naming both the missing-value flag and the offending token. 2. **--platform runtime validation** (PR thread on line 103): `nextArg("--platform") as Platform` was an unsafe cast — typos and unsupported values flowed straight into output paths and archive headers. New `ALLOWED_PLATFORMS` set gates the cast; invalid input exits 1 with the allowed list in the error message. Smoke-tested all three paths: | Command shape | Output | |---|---| | `--platform badvalue` | `Invalid --platform "badvalue". Allowed: grok, chatgpt, ...` | | `--ai-name --platform grok` | `Missing value for --ai-name (next token "--platform" looks like a flag)` | | `--ai-name Foo --platform grok --topic test --dry-run` | proceeds, fails later at missing input — correct | Verified: `bunx tsc --noEmit` clean. Co-Authored-By: Claude --- tools/save-ai-memory/process-extract.ts | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tools/save-ai-memory/process-extract.ts b/tools/save-ai-memory/process-extract.ts index d295b26d1..ac9b67875 100644 --- a/tools/save-ai-memory/process-extract.ts +++ b/tools/save-ai-memory/process-extract.ts @@ -65,6 +65,15 @@ import { execFileSync } from "node:child_process"; type Platform = "grok" | "chatgpt" | "claudeai" | "gemini" | "deepseek" | "unknown"; +const ALLOWED_PLATFORMS: ReadonlySet = new Set([ + "grok", + "chatgpt", + "claudeai", + "gemini", + "deepseek", + "unknown", +]); + interface Args { aiName: string; platform: Platform; @@ -90,6 +99,10 @@ function parseArgs(argv: string[]): Args { console.error(`Missing value for ${name}`); process.exit(1); } + if (v.startsWith("--")) { + console.error(`Missing value for ${name} (next token "${v}" looks like a flag)`); + process.exit(1); + } return v; } for (; i < argv.length; i++) { @@ -98,9 +111,17 @@ function parseArgs(argv: string[]): Args { case "--ai-name": args.aiName = nextArg("--ai-name"); break; - case "--platform": - args.platform = nextArg("--platform") as Platform; + case "--platform": { + const raw = nextArg("--platform"); + if (!ALLOWED_PLATFORMS.has(raw)) { + console.error( + `Invalid --platform "${raw}". Allowed: ${[...ALLOWED_PLATFORMS].join(", ")}`, + ); + process.exit(1); + } + args.platform = raw as Platform; break; + } case "--topic": args.topic = nextArg("--topic"); break;