Skip to content

test(spec): correct the row-update positive control's data-API path - #17581

Merged
os-bill merged 1 commit into
mainfrom
claude/issue-15099-action-row-update-positive-control-path
Sep 11, 2026
Merged

os-bill merged 1 commit into
mainfrom
claude/issue-15099-action-row-update-positive-control-path

Conversation

@os-bill

@os-bill os-bill commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Fixes #15099

Clause-②: no — re-derived from what this ships, not inherited. The diff is one string literal inside a .test.ts. No accept set moves (target is z.string().optional() and ActionSchema is untouched), no authorable key is added or removed, no closed-set member, no published export, no registry entry. C5 widening tells: none.

What changed

One line — packages/spec/src/ui/action-row-update.test.ts:156, the positive control of the row-level declarative-write suite:

-      name: 'revoke', label: 'Revoke', type: 'api', target: '/api/v1/sys_api_key/{id}',
+      name: 'revoke', label: 'Revoke', type: 'api', target: '/api/v1/data/sys_api_key/${ctx.recordId}',

Two things were wrong with the old spelling, and both are corrected:

  1. The /data segment. The shipped data door is PATCH {apiBase}/data/:object/:id — getApiBasePath() composed with crud.dataPrefix, whose default is /data (packages/spec/src/api/rest-server.zod.ts:312). The fixture omitted it.
  2. The placeholder convention. target documents ${param.X} / ${ctx.X} interpolation (action.zod.ts:985-1003); a bare {id} is newTabUrl's convention and is never substituted here — the method docblock says so in as many words.

The new value is byte-identical in shape to the corrected twin at packages/spec/src/ui/action.zod.ts:1403, which PR #15098 landed.

Direction

Taken from the newest triage comment, quoted rather than paraphrased:

⚠️ ⛔ Do not "fix" this by making the schema validate data-API paths. That would be a new refusal on a published schema, not this card. Correct the control's spelling.

The card's alternative — replace the value with something obviously illustrative — was not taken. That option was licensed only on a measurement that correcting the spelling is actively misleading, and the measurement says the opposite: after this change the fixture agrees byte-for-byte with its twin docblock at action.zod.ts:1403, and no source file under packages/** spells the pre-fix shape any more — the sole surviving occurrence is the CHANGELOG.md historical record. A reader grepping sys_api_key for a worked example now finds one answer instead of two.

Premise, falsified on this branch's own merge base (f721ef0ff2)

Readings taken on the source tree at the merge base, before any build. Occurrence counts, not line counts — grep -o ... | wc -l, because grep -c counts lines and would have made these readings void.

reading measured
/api/v1/sys_api_key across packages/** before the fix 2 — action-row-update.test.ts:156 and packages/spec/CHANGELOG.md:8775
lit control on the same corpus, same command shape: /api/v1/data/sys_api_key 12 occurrences across 7 files ⇒ the reading above is a reading, not an empty corpus
sys_api_key occurrences inside the edited file 1 ⇒ one string, one site
target's schema z.string().optional() — no path validation

packages/spec/CHANGELOG.md:8775 is a historical record and is not rewritten. It is the only place the old spelling survives, and a changelog is true about the past.

The ablation this card owed: the fixture is INERT to the assertion

Three legs, same command, same file, same suite. Every mutation and every restore proven by state — git hash-object against the HEAD blob and an empty git diff HEAD — never by an exit code. The mutating script carried trap restore EXIT INT TERM with an absolute path, and each mutation was confirmed on disk by an anchored grep -c of both the injected and the removed text before the run.

HEAD blob for packages/spec/src/ui/action-row-update.test.ts = 09871ea6859d4f7de5f25ab0e6467579c40da2cb

LEG 0  BASELINE at HEAD (the corrected fixture)
       on-disk hash 09871ea685... == HEAD blob
       Test Files  1 passed (1)      Tests  31 passed (31)          exit 0

LEG 1  ABLATION — target := 'ABSURD::::not-a-url::::@@@@/nowhere/{{{'
       anchor BEFORE: old=1 new=0   AFTER: old=0 new=1
       on-disk hash 4f9a15b689... != HEAD blob   (the mutation really landed)
       Test Files  1 passed (1)      Tests  31 passed (31)          exit 0   ⇒ INERT
       RESTORE PROVEN BY STATE: hash=09871ea685... == HEAD blob; git diff HEAD empty

LEG 2  LIT CONTROL — type: 'api' := 'NOT_AN_ACTION_TYPE' in the SAME test
       anchor BEFORE: old=1 new=0   AFTER: old=0 new=1
       on-disk hash 085f8a7edd... != HEAD blob
       × positive control — the api and script forms are untouched by the new refinements
       Test Files  1 failed (1)      Tests  1 failed | 30 passed (31)   exit 1   ⇒ CAN redden
       RESTORE PROVEN BY STATE: hash=09871ea685... == HEAD blob; git diff HEAD empty

Leg 1 paired with leg 2 is the proof: an absurd path leaves 31/31 green, while breaking a value the same test actually asserts reddens exactly that test. Nothing pins the path string, so the card's premise stands and this is a commit rather than a report. It is also why this sat unnoticed: a positive control teaches with the authority of a passing test, and this one could never have failed.

Both legs restored; the working tree is clean and no ablation artifact is committed.

Changeset: measured, not asserted — skip-changeset

The card argued the fixture does not publish. Measured on the built package rather than inferred from the glob:

  • npm pack --dry-run --json on a freshly built @objectstack/spec enumerates 2011 files. src/ui/action-row-update.test.ts is absent; so is every other .test.ts (0 of 2011).
  • Lit control on that same enumeration: its published twin src/ui/action.zod.ts is present ⇒ the absence above is a reading, not an empty corpus.
  • Content, not just filenames: a token unique to the edited file (the api and script forms are untouched) scores 0 occurrences across all 2011 published files, while a control token from published source (Supports ${param.X} and ${ctx.X} interpolation) scores 61 on the same corpus. No generated artifact — json-schema, api-surface, spec-changes.json, llms.txt, prompts, liveness, dist — re-authors the test file's content.
  • The one published occurrence of the old spelling is CHANGELOG.md, the historical record this PR deliberately does not touch.

Nothing already published moves ⇒ no changeset.

Verification

Run on the final commit 2b266a8ced.

check result
pnpm --filter @objectstack/spec exec vitest run --project local src/ui/action-row-update.test.ts 1 file / 31 tests passed
pnpm --filter @objectstack/spec test 472 test files / 13368 tests passed
pnpm --filter @objectstack/spec typecheck exit 0 — all three legs (tsc --noEmit, check:scripts-typecheck, check:test-typecheck)
the edited file is really in the typecheck program tsc -p tsconfig.test.json --listFiles names it 1x (control: 126 src/ui/ files in the same program) — so the typecheck above is not silently excluding it
pnpm exec turbo run build --filter=@objectstack/spec --filter=@objectstack/formula --filter=@objectstack/lint 4/4 successful; wrote no tracked file
pnpm lint (repo-wide, eslint . --no-inline-config) exit 0 — the full population, so no narrowing argument is needed
pnpm check:nul-bytes OK — 8338 text files scanned, no raw control bytes
derived gate families (scripts/pm/dispatch-gates.mjs --commands) 76 derived; --ran reconciles 71 run green, 5 NOT MEASURED, 0 unrun

The dependency-closure build (@objectstack/spec^...) is empty — packages/spec has no workspace dependencies — so that leg is vacuous here and the package was built directly instead.

Declared to CI, NOT MEASURED locally (exit 3, PREREQUISITE NOT MET, each needs the whole repo built): check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure, check:sourcemap-no-sources-content, check:type-check-debt. Six further gates first read as non-zero for the same reason and are green after the build: check:api-surface, check:browser-reachable-entries, check:dual-source-exports, check:entry-nameability, check:exported-any, check:doc-formula-expressions. None is recorded as a red.

验收备注

Fences held. ActionSchema is untouched; packages/spec/CHANGELOG.md is untouched; the three sibling rounds' surfaces (zod-issues-to-fields.ts, search-fields.ts, contracts/**) are untouched. The live fence on this file was checked directly: the edit is the target literal in a .success positive control, not a union-branch author-visible message pin — the message-pinning assertions elsewhere in the file are unchanged.

No out-of-scope findings were filed. One observation, noted and deliberately not filed: recordIdParam: 'id' sits beside the corrected target in the same fixture and is equally inert to the assertion, but it is a separate key with its own meaning rather than part of the path, so correcting it was outside the licensed diff and there is no evidence it is wrong. Carrier if it ever matters: whichever PR next revisits this fixture — currently none.


Generated by Claude Code

`action-row-update.test.ts`'s positive control spelled
`/api/v1/sys_api_key/{id}` — the pre-fix data-API path, missing the
`/data` segment, with a `{id}` placeholder that is `newTabUrl`'s
convention and is not substituted into `target`. The shipped door is
`PATCH {apiBase}/data/:object/:id` (`getApiBasePath()` + `crud.dataPrefix`,
default `/data`), and the twin docblock on `method` was corrected to
`/api/v1/data/sys_api_key/${ctx.recordId}`.

`target` is `z.string().optional()`, so the value carries no assertion
weight and nothing ever reddened. The cost is that the *positive control*
— the line a reader copies to learn the correct shape — taught the wrong
path with the authority of a passing test, and was the last live site in
`packages/**` spelling it.

Fixture string only. The schema is untouched: validating data-API paths
would be a new refusal on a published schema.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
@os-bill os-bill added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 11, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 216b066828b377e84c33b2e480e767b68b5b6e89 → packageMentionDocs.

os-bill commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator Author

Seat acceptance — content reviewed, ①②③ read; ④ outstanding. domain:spec execution seat, session_01MkQhmuuJAVDjmeWNixwDDH, 2026-09-11T00:25Z, head 2b266a8ced.

pre-check reading
① at-tier review not required — Clause-②: no; reviewed at default tier by this seat, on the diff
② carriers --pair 17581 → exit 0, both carriers agree, no widening tell
③ governed --test packages/spec/src/ui/action-row-update.test.ts → exit 0, NOT governed (0 of 1 on the register)
④ checks head 2b266a8ced: 31 check NAMES, 0 non-green, 16 in progress at 00:08Z

The diff is one line: target: '/api/v1/sys_api_key/{id}' → '/api/v1/data/sys_api_key/${ctx.recordId}'. Both defects in one move — the missing /data segment and the placeholder convention ({id} is newTabUrl's form; target documents ${ctx.X} / ${param.X}) — and the result is shape-identical to the corrected twin docblock at action.zod.ts:1403 that PR #15098 landed. ActionSchema untouched, as triage required.

⭐ The round declined the card's alternative, and declined it for the right kind of reason. The card offered a second option — replace the path with something obviously illustrative so it can never be mistaken for a worked example — and the earlier triage comment noted it scores better on the anti-AI-error axis. The order allowed it only on a measurement showing option 1 to be actively misleading. The round measured and found the opposite: after this fix no source file under packages/** spells the pre-fix shape at all, the sole survivor being the fenced CHANGELOG.md:8775 historical record. ⇒ There is no second wrong example left to be confused with, so the "two examples, one right one wrong, nothing marking which" cost the card is about is simply gone. ⛔ It took the default direction because the evidence pointed there, not because the default was easier.

⭐ The ablation this card actually owed was run, and it is the interesting half. The card's premise is that target is z.string() so the fixture is inert — which is why nobody noticed. The round drove that to a reading instead of asserting it:

LEG 0  baseline at HEAD                      31 passed, exit 0
LEG 1  target := 'ABSURD::::not-a-url::::@@@@/nowhere/{{{'
       anchors before old=1 new=0, after old=0 new=1; on-disk hash 4f9a15b689 ≠ HEAD blob
                                             31 passed, exit 0   ⇒ the string IS inert
LEG 2  LIT CONTROL: type:'api' := 'NOT_AN_ACTION_TYPE', same test
       anchors proven the same way; hash 085f8a7edd ≠ HEAD blob
                                             1 failed | 30 passed, exit 1   ⇒ the suite CAN redden
RESTORE hash == HEAD blob 09871ea685, `git diff HEAD` empty — proven by STATE

⚠️ Leg 1 alone would have been worthless: "nothing went red" is indistinguishable from "nothing ran". The lit control is what makes the inertness a measurement. ⇒ Nothing pins that string, so the card stays a commit and ⛔ not a report.

skip-changeset, measured on the published artifact rather than argued from files[]. npm pack --dry-run --json on the freshly built package enumerates 2011 files; the edited file is absent and so is every other .test.ts — 0 of 2011 — with its published twin src/ui/action.zod.ts present as the lit control, so the absence is a reading and not an empty corpus. ⭐ And then the content check, which is the part most rounds skip: a token unique to the test file scores 0 across all 2011 published files while a control token from published source scores 61 on the same corpus ⇒ no generated artifact (json-schema, api-surface, spec-changes.json, llms.txt, prompts, liveness, dist) re-authors it. Nothing already published moves.

⚠️ Occurrence counts throughout used grep -o | wc -l, ⛔ never grep -c, which counts lines and would have voided the readings. The label write was an additive REST POST confirmed by a comparative read-back, so the concurrent size-labeler's write was neither stripped nor stripping.

⛔ Nothing is enqueued and no auto-merge is armed until ④ is green. ⚠️ Note the standing blocker recorded on PR #17572: with the MCP identity rate-limited, this seat currently has no channel to flip a draft to ready — REST answers 200 and silently does nothing, GraphQL is refused 403. That will apply to this PR too when its checks finish.


Generated by Claude Code

@os-bill
os-bill marked this pull request as ready for review September 11, 2026 01:17
@os-bill
os-bill enabled auto-merge September 11, 2026 01:17
@os-bill
os-bill added this pull request to the merge queue Sep 11, 2026
Merged via the queue into main with commit 2f8ad09 Sep 11, 2026
40 checks passed
@os-bill
os-bill deleted the claude/issue-15099-action-row-update-positive-control-path branch September 11, 2026 01:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

protocol:ui size/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

action-row-update.test.ts positive control still spells the pre-fix data-API path /api/v1/sys_api_key/{id}

2 participants