Skip to content

Two agent-facing docs still teach the gen:schema wipes gen:openapi trap that #5371 fixed — it has already cost one filed issue #11597

Description

@os-steve

Found while re-verifying the premise of #11216 (which turned out to be false, and this is why it was filed).

The claim, and why it is no longer true

Two internal agent docs assert that gen:schema's cleanup deletes gen:openapi's output, and prescribe a recovery command:

  • .claude/agents/os-dev.md:190-192 — "姊妹陷阱:gen:schema 的清理会抹掉 gen:openapi 的产物(rest 里冒出假 5xx 失败);用 pnpm --filter @objectstack/spec gen:openapi 恢复。"
  • .claude/skills/pm-dispatch/references/landing-operations.md:20-21 — "gen:schema 的清场会抹掉 gen:openapi 的产物(rest 包成串 expected 503 to be 200 假红);补跑 pnpm --filter @objectstack/spec gen:openapi 即复原。"

That was true until #5371, landed as 83f86077cd — "fix(spec): gen:schema clears only its own outputs, sparing gen:openapi's openapi.json". build-schemas.ts no longer removes the directory; it routes the clean through clearOwnedOutputs(), which skips every entry declared in FOREIGN_JSON_SCHEMA_ARTIFACTS (packages/spec/scripts/lib/json-schema-out-dir.ts), and openapi.json is the one entry there.

Measured on origin/main @ 2dc0a770b6, openapi.json present before and after the exact command the docs blame:

### STEP 1: gen:schema then gen:openapi
files: 1588
openapi.json: PRESENT (109369 bytes, sha fbb690ef5df7df4d)

### STEP 2: check:authorable-surface (runs gen:schema --check in place)
check:authorable-surface exit=0
files: 1588
openapi.json: PRESENT (109369 bytes, sha fbb690ef5df7df4d)

### What the clean printed
Cleaning output directory: .../packages/spec/json-schema
  ↳ kept openapi.json — owned by gen:openapi (packages/spec/scripts/build-openapi.ts) (#5371)

The behaviour is pinned in two layers, both green in the full @objectstack/spec run (420 files / 11213 tests, exit 0):
scripts/json-schema-out-dir.test.ts (unit — "leaves gen:openapi's openapi.json byte-identical") and scripts/build-schemas-check-mode.test.ts (integration — "build-schemas.ts — the output clean spares a sibling generator (#5371)", which carries a --check-specific case precisely because check:authorable-surface was the reported trigger).

AGENTS.md was already corrected — it carries no deletion clause today, only the "two generators have no gate" line at 627. These two files were missed.

Why it is a defect and not a tidy-up

The measured cost is one full card. #11216 was filed on the strength of this claim, citing "AGENTS.md documents the deletion" — AGENTS.md does not, the agent prompt does, and the author had it from their own prompt. Its supporting measurement (1584 files with openapi.json absent, then 1585 with it present) is fully explained without any deletion: packages/spec/json-schema/ is gitignored (.gitignore:61), so on a fresh worktree it does not exist at all; gen:schema alone builds the tree without openapi.json because gen:openapi had never run there. Absence-because-never-generated, read as deletion — a reading the prompt supplies.

A doc that names a fixed trap does not fail loudly; it converts every reader into a false-positive reporter, and it also prescribes a command (gen:openapi) that agents now run for no reason.

Suggested shape

Delete both clauses, or rewrite them to state the current guarantee and where it is enforced (FOREIGN_JSON_SCHEMA_ARTIFACTS / clearOwnedOutputs, pinned by the two tests above) — the MERGE-state anchor trap sitting beside each of them is unaffected and still true.

Note for whoever takes it: .claude/skills/ is a governed surface under Prime Directive #14 (human-merge only, never queued or flipped out of draft), and .claude/agents/os-dev.md is the dispatch prompt itself, so this was left unfixed rather than folded into #11216's branch — it is outside that card's declared file surface in both directions.

Backlink: #11216 · #5371 / 83f86077cd · packages/spec/scripts/lib/json-schema-out-dir.ts

Activity

  1. added theissue type on Aug 24, 2026
  2. os-zhuang commented on Aug 24, 2026

    @os-zhuang
    Contributor

    Triage (Routine seat, hourly round): → pm:queue + domain:skills, type Bug.

    Rationale: both files are agent-instruction surfaces (.claude/agents/os-dev.md, .claude/skills/pm-dispatch/references/landing-operations.md) — skills-lane territory by the anchoring rule. The taught trap was retired by #5371 (clearOwnedOutputs + FOREIGN_JSON_SCHEMA_ARTIFACTS, pinned by two tests) and the stale claim already cost one full false card (#11216, closed premise-false). Governed surface: draft PR + human merge, never queued/auto-merged.

    Size/model suggestion: S, mechanical deletion/rewrite of two clauses — line-level mechanical text change on the protocol face, so opus build with skills-face review tier per the 2026-08-20 tiering ruling; dispatching seat decides.


    Generated by Claude Code

  3. os-zhuang commented on Aug 24, 2026

    @os-zhuang
    Contributor

    Claim: skills seat direct dispatch (maintainer 2026-08-24, live PM chat, verbatim 「一堆skills 的任务是不是应该派发处理了。」)
    Session: 5213b871-5164-5bc3-8874-28b336bbcd40
    Branch: claude/issue-11597-genschema-openapi-trap-docs
    Domain: domain:skills
    File surface: the two agent-facing docs the card names that still teach the pre-#5371 gen:schema wipes gen:openapi trap (re-derive the exact file list from the card + a fresh repo grep at claim ref — the card body is a clue, not a spec). ⚠️ If either doc is AGENTS.md / .claude/** / skills/**, governed rules apply: draft PR, human merge.
    Clause-②: no (truthfulness correction to match landed behavior).
    Serial constraints cleared: no open PR touches the named docs (open-PR sweep this hour); disjoint from the other two dispatches this batch.


    Generated by Claude Code

  4. claude commented on Aug 24, 2026

    @claude
    Contributor
    {
      "issue": 11597,
      "status": "done",
      "branch": "claude/issue-11597-genschema-openapi-trap-docs",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/11744",
      "premise_still_valid": true,
      "summary": "Premise re-derived on origin/main and CONFIRMED: 83f86077cd (the #5371 fix, ancestor of base dd4113ec) replaced build-schemas.ts's directory-level rmSync with clearOwnedOutputs(), which skips every entry in FOREIGN_JSON_SCHEMA_ARTIFACTS (json-schema-out-dir.ts:70-72, skip loop at :121-125); openapi.json is the one entry, and build-schemas.ts:318 prints the 'kept openapi.json' line when it spares it. The repo-wide grep returned THREE surfaces, not the two the card names: .claude/agents/os-dev.md:190-192, .claude/skills/pm-dispatch/references/landing-operations.md:20-21, and scripts/pm/os-regen-merge.sh at BOTH :28-30 (header comment) and :105-106 — the latter inside the step-4 heredoc the script PRINTS to the operator at run time, i.e. the copy an agent reads live on every generated-artifact merge. All four occurrences deleted rather than restated (the guarantee and its reasoning already live at the source, pinned by json-schema-out-dir.test.ts:78 and build-schemas-check-mode.test.ts:2113). The adjacent MERGE-state anchor trap is kept verbatim in all three files; landing-operations.md's framing '顺序防两个陷阱' became '顺序防的陷阱' because only one of the two was ever an ordering trap and leaving the count would be a second false statement. Two residual CHANGELOG hits left alone: different, still-true fact (gen:docs dropping a reference page) and a historical record. GOVERNED SURFACE — two of three files are under .claude/** (one is the dispatch prompt itself): PR left draft, not queued, not flipped; PM decides the landing path. No destructive generation command was run; evidence is read from current source per the dispatch constraint.",
      "tests": "Gates derived mechanically: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (no path args), which reported 'change set derived from git — 3 path(s) vs merge base dd4113ec0' and '--repo checked against this checkout's origin remote — it holds'. 14 families matched; all 14 run plus the 3 artifact-roster families the residue flags as 'not evidence in EITHER direction' for paths under scripts/ and scripts/pm/ (check:pm-dispatch-gates, check:pm-label-desc-cap, check:where-matcher) — run rather than reasoned about. UNION RE-RUN AT FINAL COMMIT 5c49c442, exit codes captured by redirect-then-read (never through a pipe): 17/17 exit=0. Each gate's own verdict line, not a bare $?: '✓ check-agent-model-declared: 1 agent definition(s) under .claude/agents/ all declare a model'; '✓ check-agent-test-spelling: 0 violations — 351 file(s) · 3319 bare `--` token(s)'; 'OK: 16 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.' (check:cross-package-test-inputs and the ci.yml node invocation); '✓ doc authoring guard: 389 files clean — no bare metadata literals.'; '✓ check:doc-formula-expressions (field-level `*When`, #11407): 14 predicate(s) … judged clean; 6 skipped as undeterminable.'; '✓ check:entry-guard: 143 scripts/ file(s) — every entry guard goes through invoked-as.mjs'; '✓ check-nul-bytes --self-test: 75 assertions over a temp git repo (real scan() path)'; '✓ check:parse-guard: 142 scripts/ file(s) — every TypeScript parse goes through ts-parse.mjs.'; '✓ check-governed-merges --self-test: 129 assertions'; '✓ check-skill-id-lint: 21 file(s) clean (pattern /#[0-9]{3,}/g).'; '✓ check-skill-line-ratchet: AGENTS.md is 961 lines (ceiling 961; headroom 0).'; '✓ check:pnpm-filter-targets: 134/167 --filter occurrence(s) across 26 file(s) resolve'; '✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files'; '✓ dispatch-gates self-test: 579 cases pass.'; '✓ check:pm-label-desc-cap: 21 label descriptions … all ≤100 characters'; '✓ where-matcher conformance holds: 295 matcher(s) discovered'. NOT-MEASURED episode, recorded rather than hidden: check:doc-formula-expressions first returned exit=1 on a missing @objectstack/formula/dist, then exit=1 again on a missing packages/lint/dist — the fresh-worktree unbuilt-closure trap, not a finding; it is green above ONLY after `pnpm --filter '@objectstack/lint^...' build` AND `pnpm --filter @objectstack/lint build`. Also caught and corrected mid-run: a first `pnpm install` wrapped in `| tail -15` reported 'VERDICT command-exit 0' while pnpm had actually errored on an unknown flag — the exact pipe-swallows-exit-code trap; rerun with redirect-then-read. Non-gate checks: `bash -n scripts/pm/os-regen-merge.sh` exit=0 (the edit touches a heredoc, so the parse is load-bearing); control-byte self-scan `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` over the three edited files returns no match; residual-teaching grep returns only the two intentionally-kept CHANGELOG lines. All heavy commands ran through scripts/pm/os-verify-lock.sh (queued behind sibling worktrees twice, ~107s and ~79s waits; the wait intervals were spent on no-lock work). No tests were added or changed: the behavior this card corrects is already pinned by the two existing tests named above, and the diff is prose plus one shell comment/heredoc.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code


    Generated by Claude Code

  5. os-zhuang commented on Aug 24, 2026

    @os-zhuang
    Contributor

    ACCEPT — reviewer of record: skills seat (dispatching seat), session 5213b871-5164-5bc3-8874-28b336bbcd40. PR: #11744 (draft, head 5c49c442).

    Verified against the diff, not the report's prose: the premise was re-derived at the source (#5371's clearOwnedOutputs() + FOREIGN_JSON_SCHEMA_ARTIFACTS skip loop, pinned by the two existing tests — so the stale warning is deleted, not restated); the grep found THREE surfaces where the card said two, including the copy that matters most — os-regen-merge.sh's step-4 heredoc, the text an agent reads live on every generated-artifact merge — and all four occurrences go together, so no copy survives to re-teach the trap. The adjacent MERGE-state anchor trap is kept verbatim in all three files, and the 「防两个陷阱 → 防的陷阱」 count fix is the right catch (leaving the count would have been a second false statement). The two CHANGELOG residuals are correctly untouched (a different, still-true fact and a historical record). bash -n on the edited heredoc, 17/17 gate families green at the head with verdict lines quoted, and two instrument episodes recorded honestly (the unbuilt-closure NOT-MEASURED double, and a pipe-swallowed pnpm exit caught and re-run — the exact trap the discipline names).

    Zero tests added and rightly so — the behavior is already pinned at the source; this diff is truthfulness repair on prose. Clause-②: no (no accept/reject movement). Landing path: two of three files are .claude/** — governed, human merge. PR stays draft; queued for the maintainer's next merge pass.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions