Repository navigation
fix(cli): os meta delete says nothing was deleted when the reset door answers reset: false - #23557
Conversation
…he reset door answers reset: false Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): ⛔ 1 release-owned page(s) name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 28 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 8c35679af61c806b565bde823e8b0965502e1c58 && git checkout 8c35679af61c806b565bde823e8b0965502e1c58
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b57228913aecc0988f704dbf6b9549c3324d8ebc 41060487d3ec9e7281ece57eab5c07f3bf0061e8 && git checkout -B drift-repro b57228913aecc0988f704dbf6b9549c3324d8ebc && git merge --no-ff 41060487d3ec9e7281ece57eab5c07f3bf0061e8
node scripts/docs-audit/affected-docs.mjs --json b57228913aecc0988f704dbf6b9549c3324d8ebc
|
席位核对✅ ACCEPT: PR #23557 at
Checklist, read against GitHub and
Accepted deviations:
Out-of-scope, both Acceptance notes, 「承接者:无」:
Landing: Generated by Claude Code |
Fixes #23479
Clause-②: no
What changed
os meta delete's default (human) output now reads the reset door'sresetflag. The--format jsonand--format yamloutputs already report that flag asdeleted. Until now the human output printed its success line without reading it.packages/cli/src/commands/meta/delete.ts,run()'s human branch:reset: falsemeans the delete's address held no row, so nothing was removed. In that case the command prints a warning line, built from the flags it sent:os meta delete view x, nothing stored✓ Metadata deleted: view/x⚠ Nothing deleted: view/x — the server found nothing to delete... --package P, P holds no row of it✓ Metadata deleted: view/x⚠ Nothing deleted: package 'P' holds no row of view/x... --draft, no pending draft✓ Pending draft discarded: view/x⚠ Nothing discarded: view/x has no pending draft... --draft --package P, P holds no pending draft of it✓ Pending draft discarded: view/x⚠ Nothing discarded: package 'P' holds no pending draft of view/xreset: true: the success line is unchanged, byte for byte, including with--package.=== false, not as falsiness.falseis the door's positive "nothing was removed" (DeleteMetaItemResponseSchema). An absent flag carries no signal, and reading it as "nothing deleted" would deny a deletion that really happened. The siblingos data deletereads its flag the same way ([finding]os data delete的默认人读输出无条件打印「Record deleted」—— #19411 落地后它会与同命令的--format json臂给出相反的事实 #19413).ObjectStackProtocolImplementation.deleteMetaItem'sreset: falsereturns). With--package P, the door's receipt is "No row of TYPE/NAME is bound to package 'P', so nothing was deleted", and on--draftit is "No pending draft of TYPE/NAME is bound to package 'P'". The command names no cause it did not read: it does not say "already at artifact default", and it does not say which package holds the row instead. The package id is quoted verbatim, as it was sent.The no-op exit code: measured, unchanged (
0)The command already declares a no-op as a completed command that exits
0:success: true(the CLI envelope's "the command completed") besidedeleted: false. The[#13023]note inrun()defines the two booleans that way.delete-reset-carriers.test.tsalready pins two real-door no-ops at exit0: "NAMING A PACKAGE THAT HOLDS NO ROW" (--format json,deleted: false) and "--draftparses" (a--draftreset ofview/nothing_here).0on all five no-op runs.content/docs/deployment/cli.mdxdeclares a different code foros meta delete.cli.mdxhas noos meta deleteentry.So the exit code stays
0on both values and all three formats. Moving only the human output off0would re-create the disagreement one layer down. Moving all three would narrow a published CLI accept set, so it is not done here. The siblingos data deletemade the same call for the same reason (#19413). Failures (a refusal or a transport error) still exit1, and the pin file has a control for that.Pins:
packages/cli/src/commands/meta/delete-noop-line.test.ts(unit tier)The pins drive the real oclif command and a real
ObjectStackClientwhosefetchreturns a canned receipt from the door. The receipts use the shapes and sentences of the door's own delete and no-op returns. There are four argv variants: no flag,--package,--draft, and--draft --package.resetintact.reset: true: each variant's whole output equals its success line, and the exit code is0. Negative control: noNothing deletedorNothing discardedtext appears.reset: false: each variant's whole output equals its no-op line, and the exit code is0. Negative control: noMetadata deleted, noPending draft discarded, and no success glyph.--package Pnames P, verbatim, and only P. Negative control: the package-less no-op line names no package.deleted, YAMLdeleted, and the human line agree for every variant and both values.humanClaimrefuses an output that says neither sentence, or both.0on both values × three formats × four variants. Control: a 500 from the transport still exits1.Ablation (one-off, not kept)
The fix was committed first. Then
node scripts/ablation-replace.mjsmutated the anchorif (result.reset === false) {toif (false) {, which restores the unconditional success line. It ran the pin file in WRAP mode under the verify lock:The direction is red, as predicted. The three cases that read the
reset: falsebranch go red. The four that do not (the control, thereset: truepin, the exit-code pin and the transport control) stay green. The subject is imported relatively (./delete.js, fromsrc), so nodist/rebuild was involved.Real-door probe (one-off, not kept)
This is the card's two repros plus the draft no-op and a delete-ful control. They ran through the real
RestServer,ObjectStackProtocolImplementationand SQLitesys_metadata, using the harness fromdelete-reset-carriers.test.ts, on headc52520177f:In the same run,
delete-reset-carriers.test.ts(integration tier, real door) was green: 2 files, 34 tests.Verification
All of the following ran on head
41060487d3, after the final commit. The code has not changed since the ablation and the probe, which ran onc52520177f. Exit codes were captured before any pipe. Heavy runs went throughscripts/pm/os-verify-lock.sh.pnpm turbo run build --filter='@objectstack/cli^...' --concurrency=2built the dependency closure (VERDICT command-exit 0). A fullpnpm turbo run build --filter='!@objectstack/docs' --concurrency=2followed (VERDICT command-exit 0, 75/75 tasks), because the dist-reading gates and two CLI pins need every package built.pnpm --filter @objectstack/cli typecheck: exit 0. The new pin file is in thattscprogram:tsc --listFilesOnly -p tsconfig.jsonlistssrc/commands/meta/delete-noop-line.test.ts, andcheck:test-typecheckreports its debt ledger held.pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2: 288 of 290 files and 4295 tests passed (29 skipped). The 2 failed files weretest/published-subpath-console.pin.test.tsandtest/published-subpath-hook-body.pin.test.ts. Both refused at load with "packages/cli is not built (./dist/index.js is absent)", a prerequisite and not a finding. They were re-run after the full build: 2 files and 29 tests passed (exit 0). Theintegrationtier is declared to CI. Locally I ran onlydelete-reset-carriers.test.tsfrom it, in the probe run above: green, 2 files and 34 tests.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 64 commands, and all 64 were run. Four refused on the first pass with exit 3 (PREREQUISITE NOT MET, unbuilt dist):check:dual-build-cjs-loads,check:i18n,check:i18n-coverageandcheck:i18n-walk-parity. After the full build they were re-run with exit 0.check:dts-closure,check:lean-entry-closure,check:published-filesandcheck:sourcemap-no-sources-contentwere also re-run on the built tree: exit 0.--ranreconciliation: "64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN" (a derived zero; every line carries its exit code).pnpm lint(the whole-repoeslint . --no-inline-config): exit 0, no findings.Acceptance notes
message. The no-op receipt carries a sentence that names what remains, for example "package 'com.qa.alpha' still holds a row". The JSON and YAML outputs drop it too ({ success, type, name, deleted }). Surfacing it would be a new output key or line, which is outside this card. This is an observation, not filed.stateorpackageId. So a--draftor--packagereset there would remove whatever row it finds, and the CLI would print the--draftsuccess line. This is dormant: it is not reached from an environment kernel, and draft or packaged rows ofjoboragentare not writable. It is not filed, and is noted here for the reset-door owner.ObjectQL). The integration-tierdelete-reset-carriers.test.tsis untouched; its success-line assertions arereset: trueruns and still hold.Generated by Claude Code