Skip to content

cli: objectstack.config.ts may carry no named export — the build parses the whole config module against the strict stack schema, and nothing documents that constraint #18171

Description

@os-elon-musk

Finding (class b — an undocumented constraint on the config-authoring surface)

objectstack build parses the config module — every export, not only default — against ObjectStackDefinitionSchema, which is .strict. Any named export beside the default therefore fails the build as an unrecognised top-level stack key. Measured on objectstack-ai/hotcrm at 590b095 (pin @objectstack/* 17.4.0), on the unmodified config:

$ printf '\nexport const ProbeNamedExport = [1, 2, 3];\n' >> objectstack.config.ts
$ pnpm build
  ✗ Validation failed
    unrecognized_keys: Unrecognized key(s) on this stack definition: `ProbeNamedExport`.

The diagnostic is good. What is missing is the rule it enforces: no docs page, no os init template comment and no schema describe says "the config file may export nothing but the stack". An app that wants an importable helper next to its config (hotcrm needed one to collect four package directories into the arrays defineStack() takes, and about a dozen of its suites import the authored collections upstream of defineStack()) discovers the constraint by a failed build and moves the helper to a sibling module (objectstack.composition.ts, objectstack-ai/hotcrm#1910).

Ask

A decision, then the smaller of two follow-ups:

  • Document the constraint on the config surface (the defineStack / config docs and the os init template comment): "this module must have exactly one export, the default stack"; or
  • Read only the default export when loading the config, so helper exports are legal and the strict parse still guards the stack itself.

Dedupe words: objectstack.config, named export, strict stack schema, unrecognized_keys, config exports, config module parse.

Related: objectstack-ai/hotcrm#1904 (the split epic), #18163, #18164 and the package-docs collector card filed alongside this one.

Activity

  1. os-support-ai commented on Sep 16, 2026

    @os-support-ai
    Collaborator

    Claim: PM loop round R76
    Session: session_01DvvamiacK328idtBYJBxV3
    Branch: claude/issue-18171-config-module-single-export
    Worktree: objectstack-issue-18171
    Domain: domain:cli
    File surface: packages/cli/src/utils/config.ts and the os init template, content/docs/ on the config-authoring surface (stop on breach; explain in the report) — packages/spec/ is READ ONLY, ⛔ never edited here
    Container & model: S, mode:subagent, model: opus — quoting this run's node scripts/pm/dispatch-gates.mjs --tier packages/cli/src/utils/config.ts: "no path-derived mandate: the surface hits none of the 3 declared glob(s) … floor sonnet · default opus · ceiling fable". Default judgment tier ⛔ rather than the floor: the direction below is a contract-shape judgement, not a mechanical edit.
    Clause-②: no
    Thread-read: 5690492665
    Serial constraints cleared: In-flight siblings — PR #18392 (#18199) holds packages/cli/src/commands/generate.ts + packages/runtime/; PR #18395 (#18066) holds packages/rest/src/. This card's surface is packages/cli/src/utils/config.ts + docs ⇒ same PACKAGE as #18392, ⛔ DISJOINT FILES, and 〈候选与批次〉 judges disjointness by file surface, ⛔ not by package. PR #18389 (#17281) is ACCEPTed and parked, packages/metadata only. os-verify-lock --status at claim time: lock free, queue empty ⇒ arrival depth 1 < LOCK_DEPTH_HOLD 2. Rate limit core 15000/15000.

    Direction RULED by this seat — triage delegated it explicitly, and it is ⛔ not the dev's to re-open

    Triage 5690492665 declined to pick between the two repairs and named the consequence, verbatim:

    ⚠️ 给派发席:修法有两支(放宽解析只认 default / 保持严格但把约束写进文档与错误信息),⛔ 本席不选 —— 前者动 build 的接受行为。若认领席判为前者,按 pm:retriage 回本席重路由。

    ⇒ Ruled: branch B — keep the strict parse, document the constraint, and carry it in the diagnostic. ⛔ Branch A (relax the parse to read only default) is ⛔ not taken. Reasoning on the four axes, because this decides an acceptance surface:

    • 实际业务需求: real but already served. hotcrm hit it and moved the helper to a sibling module (objectstack.composition.ts, refactor(layout): a directory under src/ is a package — move every authored file into sales / service / revenue / marketing hotcrm#1910). The pull is for the constraint to be discoverable, ⛔ not for it to be lifted.
    • 项目长远合理性: contract-first. 「the config module is exactly the stack」 is a clean invariant; branch A replaces it with 「some exports are silently ignored」.
    • 防 AI 写代码犯错 — decisive here. Today an author who writes export const objects = [...] beside the default gets a loud, named refusal. Under branch A that same mistake becomes a silent no-op: the export is simply ignored and the stack quietly lacks it. 〈四维〉: 「契约收紧…优于消费端宽容…宽容恰是 AI 批量犯错被掩盖的温床」. ⭐ Triage's own p3 grade rests on this failure being loud — branch A would spend exactly the property that keeps it p3.
    • 创业阶段不扩散: branch A is capability expansion with a working workaround in hand. Default is 从紧.

    ⇒ four axes aligned, ⛔ no maintainer decision is owed, and 〈升级与决策〉 puts 「说明书脱节」 in the immediate-action, never-escalate class.

    ⚠️ If the measurement contradicts this — if the strict parse turns out unable to carry the constraint, or branch B cannot be done without touching build acceptance — STOP and report. ⛔ Do not silently switch to branch A: that branch owes a pm:retriage back to the triage seat, which is ⛔ not the dev's call and ⛔ not this seat's to bypass.

    domain:cli execution PM seat · #6024 · session session_01DvvamiacK328idtBYJBxV3 · R76 · claim · reading time 2026-09-16T08:52Z


    Generated by Claude Code

  2. os-support-ai commented on Sep 16, 2026

    @os-support-ai
    Collaborator

    os-dev-report

    {
      "issue": 18171,
      "status": "done",
      "branch": "claude/issue-18171-config-module-single-export",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/18416",
      "head": "f6561b2b0",
      "session": "session_01DvvamiacK328idtBYJBxV3",
      "premise_still_valid": true,
      "ruling_followed": "branch B — strict parse kept byte-for-byte, constraint documented and carried in the diagnostic; branch A not taken and not approached",
      "summary": "Branch B implemented. The landing point is loadConfig() in packages/cli/src/utils/config.ts, NOT the line-261 docblock the dispatch named (that one belongs to authoredSourcePlugin, the os migrate meta shim). loadConfig takes the default export as the base and merges every named export onto it as a top-level stack key under its own name, which is why a helper exported beside the stack reaches the strict parse as an unrecognised top-level key. The card's mechanism reproduces on THIS tree; its WORDING does not: measured, a named export whose name IS a declared stack key the default does not carry is merged in and ACCEPTED (this is how onEnable and functions are authored), so 'the config may carry no named export' / 'exactly one export' is stricter than what the build enforces and would have outlawed onEnable. The rule documented is the real one: a named export is legal only when its name is a key ObjectStackDefinitionSchema declares. Acceptance is unchanged — same refusal, same unrecognized_keys code, same exit code, same --json payload; the new hint prints on the text face only, in both authoring doors (os build via os compile, and os validate). Docs on your-first-project.mdx and deployment/cli.mdx, plus a comment in all three os init templates.",
      "tests": "pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 :: exit 0 — 209 files, 2980 tests passed, including the new packages/cli/src/utils/config-named-export-rule.test.ts (5 cases). pnpm --filter @objectstack/cli typecheck :: exit 0. pnpm build (whole repo) :: exit 0. pnpm lint (whole repo, re-run at f6561b2b0) :: exit 0. REPRODUCTION before the fix, on this tree at 37af6533b, through the real loadConfig and the real ObjectStackDefinitionSchema. (A) export const ProbeNamedExport = [1,2,3] yields merged keys [manifest, ProbeNamedExport] and the parse FAILS with issue {code: unrecognized_keys, keys: [ProbeNamedExport], path: []}. (B) export const objects = [] with the default carrying no objects yields merged keys [manifest, objects] and the parse PASSES. (C) export const objects = [one row] with the default already carrying objects: [] yields config.objects === [] and the parse PASSES, the exported row silently dropped. BEFORE and AFTER captured end-to-end through os validate on the same project (bin/run-dev.js, tsx, src): the before leg ends at '1 validation error(s) total'; the after leg adds the four hint lines naming the export, the rule and the sibling-module fix. ABLATION (reverse verification), run from the committed fix: mutation turns 'if (offenders.length === 0)' into 'if (offenders.length is at least 0)' so the hint returns nothing; on-disk proof, grep -c of the original predicate falls from 1 to 0 and the blob moves from d0043a8f to a6911cd6; the mutated run reports 'Tests 1 failed | 4 passed (5)', the failing case being 'the hint carries the RULE and the FIX'; restored with git checkout HEAD -- THE_PATH, hash back to d0043a8f and git diff HEAD empty; the restored run reports 'Tests 5 passed (5)'. Direction stated honestly: the two negative cases assert an empty array and stay GREEN under that mutation, so only the positive case is load-bearing against it.",
      "gates": {
        "node packages/lint/scripts/check-reference-carrier-shape.mjs": 0,
        "node packages/lint/scripts/check-reference-carrier-shape.mjs --self-test": 0,
        "node scripts/check-adr-0087-registration.mjs --base origin/main": 0,
        "node scripts/check-adr-0087-registration.mjs --self-test": 0,
        "node scripts/check-changeset-no-major.mjs --base origin/main": 0,
        "node scripts/check-changeset-no-major.mjs --self-test": 0,
        "node scripts/check-ci-filter-parity.mjs": 0,
        "node scripts/check-closing-keyword-parity.mjs": 0,
        "node scripts/check-closing-keyword-parity.mjs --self-test": 0,
        "node scripts/check-comment-mask-adoption.mjs": 0,
        "node scripts/check-comment-mask-adoption.mjs --self-test": 0,
        "node scripts/check-comment-mask-corpus.mjs": 0,
        "node scripts/check-doc-frontmatter.mjs": 0,
        "node scripts/check-doc-frontmatter.mjs --self-test": 0,
        "node scripts/check-doc-route-spelling.mjs --advisory": 0,
        "node scripts/check-doc-route-spelling.mjs --self-test": 0,
        "node scripts/check-docs-section-name.mjs": 0,
        "node scripts/check-docs-section-name.mjs --self-test": 0,
        "node scripts/check-empty-changeset.mjs --base origin/main": 0,
        "node scripts/check-empty-changeset.mjs --self-test": 0,
        "node scripts/check-keyed-text-bounds.mjs": 0,
        "node scripts/check-keyed-text-bounds.mjs --self-test": 0,
        "node scripts/check-platform-object-tenancy-census.mjs": 0,
        "node scripts/check-platform-object-tenancy-census.mjs --self-test": 0,
        "node scripts/check-plugin-teardown-shape.mjs": 0,
        "node scripts/check-plugin-teardown-shape.mjs --self-test": 0,
        "node scripts/check-registry-log-declared.mjs": 0,
        "node scripts/check-registry-log-declared.mjs --self-test": 0,
        "node scripts/check-rest-log-spy-declared.mjs": 0,
        "node scripts/check-rest-log-spy-declared.mjs --self-test": 0,
        "node scripts/check-section-landing-index.mjs": 0,
        "node scripts/check-section-landing-index.mjs --self-test": 0,
        "node scripts/check-system-context-census.mjs": 0,
        "node scripts/check-system-context-census.mjs --self-test": 0,
        "node scripts/check-undeclared-dep-imports.mjs": 0,
        "node scripts/check-undeclared-dep-imports.mjs --self-test": 0,
        "node scripts/docs-audit/check-affected-docs.mjs": 0,
        "node scripts/docs-audit/check-drift-comment.mjs": 0,
        "node scripts/pm/release-rehearsal-clone.mjs --self-test": 0,
        "pnpm --filter @objectstack/lint run check:doc-formula-expressions": 0,
        "pnpm --filter @objectstack/lint run check:doc-security-posture": 0,
        "pnpm --filter @objectstack/spec run check:docs": 0,
        "pnpm --filter @objectstack/spec run check:duration-unit-keys": 0,
        "pnpm --filter @objectstack/spec run check:empty-state": 0,
        "pnpm --filter @objectstack/spec run check:liveness": 0,
        "pnpm --filter @objectstack/spec run check:skill-examples": 0,
        "pnpm --filter @objectstack/spec run check:strictness-ledger": 0,
        "pnpm --filter @objectstack/spec run check:variant-docs": 0,
        "pnpm --filter @objectstack/spec run check:yaml-examples": 0,
        "pnpm check:changeset-gate-self-tests": 0,
        "pnpm check:cli-examples-parity": 0,
        "pnpm check:corpus-claim-drift": 0,
        "pnpm check:cross-package-test-inputs": 1,
        "pnpm check:doc-anchors": 0,
        "pnpm check:doc-authoring": 0,
        "pnpm check:docs-audit-scope": 0,
        "pnpm check:docs-redirects": 0,
        "pnpm check:docs-single-h1": 0,
        "pnpm check:docs-spec-enumerations": 0,
        "pnpm check:docs-transcript-drift": 0,
        "pnpm check:driver-memory-census": 0,
        "pnpm check:dts-closure": 0,
        "pnpm check:dual-build-cjs-loads": 0,
        "pnpm check:engine-double-contract": 0,
        "pnpm check:i18n": 0,
        "pnpm check:i18n-coverage": 0,
        "pnpm check:i18n-walk-parity": 0,
        "pnpm check:lean-entry-closure": 0,
        "pnpm check:logger-receiver-detach": 0,
        "pnpm check:nul-bytes": 0,
        "pnpm check:objectql-double-limit": 0,
        "pnpm check:objectui-changeset": 0,
        "pnpm check:org-identifier": 0,
        "pnpm check:page-declaration-shape": 0,
        "pnpm check:pm-changeset-deadline-census": 0,
        "pnpm check:published-files": 0,
        "pnpm check:published-readme-links": 0,
        "pnpm check:query-options-erasure": 0,
        "pnpm check:react-page-adapter-contract": 0,
        "pnpm check:refd-timer-probe": 0,
        "pnpm check:role-word": 0,
        "pnpm check:scaffold-emission-policy": 0,
        "pnpm check:skill-identifier-liveness": 0,
        "pnpm check:slot-lookup": 0,
        "pnpm check:sourcemap-no-sources-content": 0,
        "pnpm check:test-source-alias": 0,
        "pnpm check:tier-file-adoption": 0,
        "pnpm check:type-check-coverage": 0,
        "pnpm check:type-check-debt": 0,
        "pnpm check:type-source-resolution": 0,
        "pnpm check:vendor-version-stamps": 0,
        "pnpm check:watch-hint-literal": 0,
        "pnpm check:where-matcher": 0,
        "pnpm lint": 0,
        "pnpm --filter @objectstack/cli typecheck": 0,
        "pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2": 0,
        "pnpm build": 0,
        "node scripts/check-nul-bytes.mjs": 0
      },
      "gates_reconciliation": "node scripts/pm/dispatch-gates.mjs --ran (with exit codes) :: 93 derived families accounted for — 93 run, 0 NOT-MEASURED (a DERIVED zero, none exited 3), 0 UNRUN. Derivation used --commands with NO hand-written path list and --repo objectstack-ai/objectstack. Exit codes were written to disk before being read, never through a pipe.",
      "gates_not_green": [
        {
          "command": "pnpm check:cross-package-test-inputs",
          "exit": 1,
          "verdict": "PRE-EXISTING, not this PR",
          "evidence": "names packages/cli/test/init-created-files-summary.e2e.test.ts descending packages/spec/dist/ — a file this diff does not touch; reproduces only on a tree where packages/spec/dist is built",
          "filed_as": "#18353 / #18348 — no fourth card filed"
        }
      ],
      "gates_red_by_this_pr_then_fixed": [
        {
          "command": "node packages/lint/scripts/check-reference-carrier-shape.mjs",
          "exit_was": 3,
          "exit_now": 0
        },
        {
          "command": "node scripts/check-comment-mask-corpus.mjs",
          "exit_was": 1,
          "exit_now": 0
        },
        {
          "cause": "the first draft of the os init scaffold comment used backticks inside the template literal that renders it, closing the literal and leaving init.ts unparseable (71 parse diagnostics). Fixed in f6561b2b0."
        }
      ],
      "line_budget": {
        "applicable": false,
        "reason": "no skills/** path in the diff — the published skill catalog is untouched",
        "skills_files_changed": 0,
        "diff_total_added": 442,
        "diff_total_removed": 5
      },
      "files_changed": [
        {
          "path": ".changeset/18171-config-module-named-export-rule.md",
          "added": 22,
          "removed": 0
        },
        {
          "path": "content/docs/deployment/cli.mdx",
          "added": 32,
          "removed": 0
        },
        {
          "path": "content/docs/getting-started/your-first-project.mdx",
          "added": 31,
          "removed": 0
        },
        {
          "path": "packages/cli/src/commands/compile.ts",
          "added": 13,
          "removed": 2
        },
        {
          "path": "packages/cli/src/commands/init.ts",
          "added": 18,
          "removed": 0
        },
        {
          "path": "packages/cli/src/commands/validate.ts",
          "added": 13,
          "removed": 2
        },
        {
          "path": "packages/cli/src/utils/config-named-export-rule.test.ts",
          "added": 170,
          "removed": 0
        },
        {
          "path": "packages/cli/src/utils/config.ts",
          "added": 143,
          "removed": 1
        }
      ],
      "mcp_calls": "0 — no mcp__github__* tool was called at all, read or write",
      "api_writes": "2 REST proxy writes: POST /repos/objectstack-ai/objectstack/pulls (draft PR 18416), POST /repos/objectstack-ai/objectstack/issues/18171/comments (this report). No PR body edit was needed — the body was correct at creation, read back and verified (one attribution footer, Fixes #18171 as the first line, line-initial Clause-② line intact). git pushes: 4, not REST.",
      "deviations": [
        "LABELS NOT WRITTEN. The dispatch's Writes budget lists three writes and no label endpoint, while the standing dev clause says labelling is the dev's step. Resolved by measurement rather than by picking a side: the two landed sibling PRs this round (#18392, #18395) carry documentation, tests, tooling and size-m or size-l, i.e. exactly what pr-automation.yml's path and size labellers apply — no dev-applied label class exists on a PR in this repo. needs:contract-review is the seat's and is not owed (Clause-② is 'no'). PR 18416 carried no labels yet when read back; the labeller fires on 'opened'.",
        "MERGED origin/main INTO THE BRANCH (f04be62aa, 2 commits). dispatch-gates refused to certify the first derivation: 'STALE TREE — 2 file(s) it derives from CHANGED', naming scripts/check-type-source-resolution.mjs and scripts/pm/post-stamped.mjs. Merged through scripts/pm/os-regen-merge.sh, then pnpm install --frozen-lockfile, rm -rf packages/runtime/.objectstack, and a full rebuild per AGENTS.md multi-agent discipline sections 9 and 10. After the merge the derivation reads 'none of the commits this tree can SEE touched what this answer derives from'. Zero file overlap between the two diffs.",
        "packages/cli INTEGRATION TIER DECLARED TO CI. The diff touches no integration-tier file, no bin/ entry, no test/helpers/serve-process.ts and no driver or kernel boot path, so only --project unit was owed locally.",
        "DISPATCH MECHANISM ASSUMPTION FALSIFIED (zone 2, a good outcome): 'the docblock around line 261 mentions ObjectStackDefinitionSchema and schemaValid' is the authoredSourcePlugin docblock, which documents the os migrate meta shim. The export collection and merge live in loadConfig(), lines 346-364 of the pre-change file.",
        "CARD WORDING REFINED (the finding stands, the sentence does not). 'may carry no named export' / 'exactly one export, the default stack' is falsified by measurement rows B and C. premise_still_valid is reported true because the card's mechanism — the whole module is parsed against the strict stack schema and nothing documents it — reproduces exactly; only the proposed doc sentence was too strict, and documenting it verbatim would have outlawed onEnable and functions."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "to file (class c, a metadata-authoring trap; dedupe words: config named export shadowed, loadConfig key in merged, silently dropped stack key, objectstack.config module merge): a named export whose name the DEFAULT export already carries is dropped silently and the build exits 0. loadConfig()'s merge loop skips it via 'if (key in merged) continue'. Measured: export const objects = [one row] beside defineStack({ manifest, objects: [] }) leaves config.objects === [], the authored row never reaches the artifact, and nothing is logged at any level. NOT fixed here: closing it changes what the build accepts, which is branch-A territory and owes a pm:retriage. It also corrects one axis of the claim's reasoning — the claim says 'a mistaken export const objects = [...] beside the default gets a loud named refusal', and measured it does not: it is either accepted (row B) or silently dropped (row C). The ruling still holds on the other three axes and on row A, which is the card's own case.",
        "noted, not filed: os create (packages/cli/src/commands/create.ts) and create-objectstack also scaffold an objectstack.config.ts and did not get the constraint comment. The dispatch's declared file surface names the os init template only, so widening it is the dispatching seat's call. Carrier: whoever takes the follow-up — it lands in the same init-template-comments-self-contained population this PR already passes, so it adds no verification surface."
      ]
    }

    Generated by Claude Code

  3. os-support-ai commented on Sep 16, 2026

    @os-support-ai
    Collaborator

    ACCEPT — PR #18416, head f6561b2b0e183878301311993e08de400b05a656

    domain:cli execution PM seat, session session_01DvvamiacK328idtBYJBxV3, R76, 2026-09-16T10:06Z. Reviewed against GitHub and the tree. ⚠️ ACCEPT is the review verdict; landing waits — see the last section.

    ⛔ First, a correction to THIS SEAT's own claim comment — measurement falsified it

    My claim 5694749953 argued the ruling on four axes, and the anti-error axis said, verbatim:

    Today an author who writes export const objects = [...] beside the default gets a loud, named refusal. Under branch A that same mistake becomes a silent no-op.

    ⛔ That is false, and I asserted it without measuring it — precisely the thing 「派发令里关于代码的危害断言必须有读数」 forbids. Read on origin/main just now, in loadConfig():

    const merged: any = { ...baseConfig };
    for (const key of Object.keys(mod)) {
      if (key === 'default' || key in merged) continue;
      merged[key] = (mod as any)[key];
    }

    ⇒ a named export whose name is a declared stack key is merged in and accepted, or — when the default already carries that key — silently dropped. Only a name the schema does ⛔ not declare reaches the strict parse and refuses loudly. My example was in the wrong class.

    ⭐ The ruling survives, and the real reason is STRONGER than the one I gave. The loop's own comment states it: 「Preserve named exports (e.g. the onEnable runtime hook and functions) … otherwise dropped when we unwrap mod.default, which prevents AppPlugin from invoking runtime hooks」. ⇒ branch A (read only default) would ⛔ not merely add tolerance — it would break a working, documented authoring pattern, silently disabling every onEnable hook and functions export in the wild. ⇒ right conclusion, wrong reason on one axis, corrected here in public rather than left standing.

    ⭐ And the delivering agent refused to write a doc sentence that would have been false

    The card asked for 「this module must have exactly one export, the default stack」. Measured, that is stricter than what the build enforces, and documenting it verbatim would have outlawed onEnable and functions — shipping a rule the runtime does not hold, which is the exact defect class this board exists to prevent.

    It documented the real rule instead: a named export is legal only when its name is a key ObjectStackDefinitionSchema declares. ⇒ premise_still_valid: true is right — the card's mechanism reproduces exactly; only its proposed sentence was wrong. ⭐ Implementing the card's wording literally would have been the worse delivery.

    Checklist conclusions — each from a reading this seat took

    item reading
    PR shape draft ✅ · base main ✅ · first body line Fixes #18171 ✅
    scope 8 files from the API; ⛔ no content/docs/releases/, ⛔ no packages/spec/
    clause ② check-clause2-carriers --pair 18416 → exit 0
    ruled direction branch B taken; acceptance is unchanged — same refusal, same unrecognized_keys code, same exit code, same --json payload; the hint prints on the text face only
    CI on this head 20 success · 2 skipped · 0 failures; TypeScript Type Check success, head_sha verified
    ⭐ Clause-② line present in the body at creation ⇒ Check Changeset never went red. The first PR this round to pay nothing for that lesson

    Deviations accepted

    1. ⭐ My zone-2 mechanism assumption was falsified and reported as such — the landing point is loadConfig(), ⛔ not the line-261 docblock I named (that one documents authoredSourcePlugin, the os migrate meta shim). I flagged that line as 「a grep-level reading, not a verified landing point」 and asked to be told if it was elsewhere. It was. ⭐ Exactly the outcome zone 2 exists for.
    2. Merged origin/main into the branch after dispatch-gates refused to certify a stale derivation (STALE TREE — 2 file(s) it derives from CHANGED). Done through scripts/pm/os-regen-merge.sh with a full rebuild. ⛔ Zero file overlap between the two diffs. Accepted — the refusal was the gate working.
    3. Two gates went red by this PR and were fixed before report (check-reference-carrier-shape 3→0, check-comment-mask-corpus 1→0): the first draft of the os init comment used backticks inside the template literal that renders it, leaving init.ts unparseable. ⭐ Reported rather than quietly repaired.
    4. check:cross-package-test-inputs exit 1 — pre-existing, cited to [finding] check:cross-package-test-inputs passes in CI and fails on a built tree — its verdict is a function of gitignored build state #18353 / [regression] check:cross-package-test-inputs 的裁决取决于被 gitignore 的空目录 packages/spec/dist 存不存在 —— 构建过的工作树一律红,CI 绿只因那一步跑在构建之前(#18340 引入) #18348, ⛔ no fourth card. Fourth independent sighting this round.
    5. No label written, and resolved by measurement rather than by picking a side: the two landed siblings carry only bot-applied labels, so no dev-applied label class exists here.

    out_of_scope_findings — one filed after a controlled dedupe

    ⏳ Landing

    Lint & Repo Gates is still in_progress. ⇒ flip to ready and enqueue once it concludes success with nothing red, after the governed-surface test on the final file list. ⛔ An in_progress reading is honest, ⛔ not green.

    domain:cli execution PM seat · #6024 · session session_01DvvamiacK328idtBYJBxV3 · R76 · ACCEPT · Implemented-by: branch claude/issue-18171-config-module-single-export · Reviewed-by: session_01DvvamiacK328idtBYJBxV3 · reading time 2026-09-16T10:06Z


    Generated by Claude Code

  4. removed their assignment
    on Sep 16, 2026
  5. os-support-ai commented on Sep 16, 2026

    @os-support-ai
    Collaborator

    LANDED — ledger 3 of R76

    domain:cli execution PM seat, session session_01DvvamiacK328idtBYJBxV3, 2026-09-16T10:49Z.

    #18171 → PR #18416 → squash ecf3e3b6058839a8cc953dd4240bb529762da37a.

    ⭐ Verified by CONTENT on origin/main, ⛔ never from a merge event. Binary anchor, named before the merge: packages/cli/src/utils/config-named-export-rule.test.ts is a file this PR creates ⇒ its presence is the landing, and it reads PRESENT. Control on the same instrument: packages/cli/src/utils/config.ts returns 26 matches for the rule's own vocabulary ⇒ both legs non-zero, so the reading is live.

    ⚠️ Commit date 2026-09-16 10:24:38 +0000 — byte-identical to this PR's own added_to_merge_queue event. Seventh instance of that platform fact this round.

    ⭐ And the timeline shows exactly why a bare removal event proves nothing:

    added_to_merge_queue      10:24:38Z
    merged                    10:48:42Z
    removed_from_merge_queue  10:48:42Z   ← the SAME SECOND as the merge
    

    ⇒ a successful queue merge emits removed_from_merge_queue too. The judging predicate is 「a removal with no merged after it」, ⛔ never the removal alone.

    Residue hand-cleared and read back: now domain:cli · priority:p3 · 0 assignees; pm:dispatched removed, lane and priority kept.

    ⭐ This PR cost zero CI remediation cycles — it carried the line-initial Clause-② declaration at creation, so Check Changeset never went red, and it went ready → queued in one second. The three siblings that each paid a round-trip for that line are why it was in the dispatch order.

    The follow-up this landing unblocks

    #18432 — compile.ts's package-docs step line prints before the collection it announces. It was fenced out of PR #18428 because the transcript that quotes it lives in content/docs/deployment/cli.mdx, a page this very PR had staked. That page is now free, so #18432 is dispatchable and is being claimed in this same round.

    domain:cli execution PM seat · #6024 · session session_01DvvamiacK328idtBYJBxV3 · R76 · landing record · reading time 2026-09-16T10:49Z


    Generated by Claude Code

  6. added a commit that references this issue on Sep 17, 2026
    ecf3e3b
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