Skip to content

feat(spec)!: a stack's datasources collection refuses a datasource named default, the host-reserved name - #23757

Merged
objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-23634-stack-datasource-default-refused
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-23634-stack-datasource-default-refused

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #23634
Clause-②: no

What

A stack's datasources collection now refuses an entry named default at authoring: defineStack, os validate and publish. This is triage's proxy ruling A (issue comment 6189356908).

default is the host's primary datasource. The runtime declares and connects it itself (DefaultDatasourcePlugin), and AppPlugin.start() refuses a stack that declares it in every boot posture. The authoring doors accepted the same stack, so its author met the refusal only after the boot's earlier plugins had started.

  • Where: a superRefine on the stack's datasources collection (STACK_DEFINITION_COLLECTIONS_SHAPE in packages/spec/src/stack.zod.ts). It is ⛔ not on DatasourceSchema, whose documents include the host's own default row. The issue is located at datasources.N.name, code custom. The map form datasources: { default: { … } } is refused the same way, because the key normalizes to name before the parse.
  • The comparison is the runtime's: name === 'default', exactly as app-plugin.ts compares it. Every other spelling (Default, ' default', …) is already refused by DatasourceSchema.name's snake_case pattern, never by this rule.
  • The text: it carries the runtime's sentence ("declares a datasource named 'default' — that name is reserved for the host's primary datasource") and the ruling's remedy: set OS_DATABASE_URL and omit the entry, or declare the datasource under another name and route objects to it (an object's datasource, or a datasourceMapping rule).
  • ADR-0087: D3 semantic entry stack-datasource-host-reserved-name-refused. The changeset .changeset/23634-stack-datasource-default-refused.md is a BREAKING minor (pre mode) with the registered disposition, the FROM → TO table and the one-line fix.
  • Dropped-refinements ledger: z.toJSONSchema() has no arm for a refinement, so the published JSON Schema stays wider than the parse at the four installed-package manifest.datasources sites. These are recorded in packages/spec/dropped-refinements.baseline.json (+4 sites, total 759 → 763), as the build requires. No arm of the closed refinement-projection.ts list fits ("an element's name is not X"). Adding one is a public-contract decision, so it is not made here.

Untouched, as the ruling and the claim require: DatasourceSchema (packages/spec/src/data/datasource.zod.ts), packages/cli/src/commands/serve.ts (PR #23631's remedy line stays as written), skills/**, and content/docs/releases/.

Fallback clause: no config that boots today is newly refused

I re-measured this on this branch, using the source CLI (bin/run-dev.js serve) on a host config that declares datasources: [{ name: 'default', driver: 'sqlite', … }]. The fixture uses defineStack(…, { strict: false }) so that the boot is what judges it.

  • With OS_DATABASE_URL=file:…: exit 1, [AppPlugin] app 'com.example.fx' declares a datasource named 'default' — that name is reserved for the host's primary datasource. …
  • With --dev: exit 1, the same refusal.

These agree with the readings dev's M2, M3 and M4 and with production M1 (PR #23631's no-storage refusal), all in the report on #23634. In-repo producers of the shape: none. The candidates git grep found are a runtime pin that feeds a raw bundle to AppPlugin (still green, 12/12), REST/metadata-door tests of the host's own row, and engine driver maps. None of them goes through the stack collection.

Pins

  • packages/spec/src/stack-datasource-host-reserved-name.test.ts (7 tests):
    • defineStack refuses the array form and the map form with STACK_SCHEMA_INVALID / 422, one issue at datasources.N.name;
    • the bare ObjectStackDefinitionSchema parse refuses it;
    • the reserved set is the runtime's;
    • CONTROL: fx_primary passes;
    • CONTROL: the host's own default row parses through DatasourceSchema.
  • packages/cli/test/stack-datasource-default-validate-door.test.ts (integration tier, 2 spawns): os validate --json exits 1 with exactly one error, custom at datasources.0.name, carrying the sentence and OS_DATABASE_URL. CONTROL: the same stack with fx_primary exits 0 with valid: true.

Verification

Measured on head a6f3e6e8, which is this branch merged with origin/main a869ea63 through scripts/pm/os-regen-merge.sh. Pre-merge readings are on a085cc9b.

  • spec:
    • pnpm --filter @objectstack/spec build: exit 0.
    • check:generated: all 14 artifacts up to date.
    • The pin file, scripts/dropped-refinements.test.ts and src/migrations/migrations.test.ts: 212/212.
    • Full pnpm --filter @objectstack/spec test on the pre-merge tree: 679 files passed. The one red was the ledger's header total, fixed in 109c9e90 and then 27/27.
    • typecheck: exit 0.
  • cli:
    • The door pin: 2/2 on the merge head.
    • vitest run --project unit: 295 files. 293 passed in the first run; the other 2 read packages/cli/dist, which was not built yet, and passed (29/29) after pnpm --filter @objectstack/cli build.
    • typecheck: exit 0.
  • runtime: src/default-datasource-plugin.test.ts 12/12. The AppPlugin refusal is unchanged.
  • Ablation (the refusal body made a no-op, if ((globalThis as any).__ablation23634 === undefined) return;, via scripts/ablation-replace.mjs):
    • Mutated: the spec build exited 0, and ablation-dist-preflight found the marker in 12 dist files. The spec pins went 4 failed / 3 passed: the four refusals red, the three controls green. The CLI door went 1 failed / 1 passed: the refusal red, the control green.
    • Restored: git diff HEAD was empty and the hash matched HEAD. The rebuild exited 0, and the preflight --absent was clean in all 232 files. Spec pins 7/7, CLI door 2/2.
    • Two earlier legs were no-ops and are not counted:
      • Deleting the .superRefine failed the mutated build on the dropped-refinements ledger, so dist/ stayed unmutated (the preflight caught it).
      • A string-literal guard was constant-folded out of dist/ (the preflight planted check read absent).
  • Derived gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 90 families for the 6 changed paths. On a085cc9b all 90 exit 0, and --ran reconciled 90 derived / 90 run / 0 NOT-MEASURED, a derived zero with every exit code recorded.
    • check-plugin-teardown-shape --self-test first exited 3 on the shallow clone and passed after fetching its pinned fixture commit.
    • check:stack-collection-maps was red on a multi-line z\n.array( that hid datasources from its census. It was fixed in a085cc9b by keeping z.array(DatasourceSchema) on one line.
    • Rerun on the merge head a6f3e6e8: the same 90 families, all exit 0. --ran reconciled 90 / 90 / 0 NOT-MEASURED, a derived zero.
  • check:adr-0087-registration: ✓, registered stack-datasource-host-reserved-name-refused (new here). check:dev-prereqs: exit 0.

Acceptance notes

  • Skills line, for the skills lane (⛔ not edited here). skills/objectstack-data/rules/datasources.md:4-6 reads: "Objects route to one via their datasource field (default: 'default'). Most apps need only the default datasource; declare more to read/write a separate or external database." "Declare more" implies that default is declared too, so it reads as an invitation. Suggested rewording: "Most apps need only the host's default datasource, which the host connects from OS_DATABASE_URL; never declare a datasource named default (the name is host-reserved and refused). Declare a datasource under its own name only to read/write a separate or external database."
  • One sentence, three sites. The runtime's refusal (app-plugin.ts), the admin door (datasource-admin-service.ts) and this one are synonymous, not shared. The shared text could be exported from spec and reused by the runtime, but that needs a packages/runtime edit, which is outside this claim. It is reported, not done.

Generated by Claude Code

…th its two controls

Claude-Session: https://claude.ai/code/session_01UvyD7nLUx7XGKKQVF37FY9
Co-authored-by: Claude <noreply@anthropic.com>
…ment in the dropped-refinements ledger

Claude-Session: https://claude.ai/code/session_01UvyD7nLUx7XGKKQVF37FY9
Co-authored-by: Claude <noreply@anthropic.com>
…rved datasource name refusal

Claude-Session: https://claude.ai/code/session_01UvyD7nLUx7XGKKQVF37FY9
Co-authored-by: Claude <noreply@anthropic.com>
…ult, with its control

Claude-Session: https://claude.ai/code/session_01UvyD7nLUx7XGKKQVF37FY9
Co-authored-by: Claude <noreply@anthropic.com>
…s four new sites

Claude-Session: https://claude.ai/code/session_01UvyD7nLUx7XGKKQVF37FY9
Co-authored-by: Claude <noreply@anthropic.com>
… line, where the stack-collection census reads it

Claude-Session: https://claude.ai/code/session_01UvyD7nLUx7XGKKQVF37FY9
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

4 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 1 changed file(s) yielded no anchor (packages/spec/dropped-refinements.baseline.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/dropped-refinements.baseline.json) — pages documenting those are invisible to this run
  • 4 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • 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.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 142 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 140238a47b892ef9234052e44267ec3739fa79c2 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from ce1a746bfdf5dc51cebc686f6312f65982c966ec — the merge of head a6f3e6e812fc97a0bc664b54004f394713f074fe into base 140238a47b892ef9234052e44267ec3739fa79c2, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ce1a746bfdf5dc51cebc686f6312f65982c966ec && git checkout ce1a746bfdf5dc51cebc686f6312f65982c966ec
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 140238a47b892ef9234052e44267ec3739fa79c2 a6f3e6e812fc97a0bc664b54004f394713f074fe && git checkout -B drift-repro 140238a47b892ef9234052e44267ec3739fa79c2 && git merge --no-ff a6f3e6e812fc97a0bc664b54004f394713f074fe

node scripts/docs-audit/affected-docs.mjs --json 140238a47b892ef9234052e44267ec3739fa79c2

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a6f3e6e812fc97a0bc664b54004f394713f074fe
Local-runs: none

Inputs: card #23634 (body and all eight comments — the proxy ruling A 6189356908 and the dev report 6205065737 included), PR #23757 (body, the six-file list, the net diff against main at this head; merge base a869ea63), and the check-runs on this head. Read-only: git object reads against origin/* and a fetched refs/review/pr-23757; nothing built, run or re-run. This record is the contract verdict; it does not stand in for a check-run conclusion, and the landing still waits for every required check to read success.

① Derived judgments

Every accept-set or public-surface change the diff implies, each judged:

  1. The stack's datasources collection refuses an element whose name is exactly default — a superRefine on z.array(DatasourceSchema) in STACK_DEFINITION_COLLECTIONS_SHAPE, one custom issue at datasources.N.name; the map form datasources: { default: … } reaches it already normalised to name. RIGHT. It is the ruling's placement (the collection, ⛔ not DatasourceSchema), the runtime's own comparison (app-plugin.ts compares name === 'default'), and an accepted decision rather than a new one: ADR-0062 (the ADR-0062 D1 收尾:default driver 的 connect 与失败判决仍是第二份实现(阻塞点已定位) #3826 second pass) records that default became a host-reserved name, rejected in app bundles at load and in runtime-admin create. The spelling set is exactly the runtime's: Default, DEFAULT and padded forms are refused by DatasourceSchema.name's snake_case pattern, never by this rule, and that is pinned.
  2. The same refusal reaches every door that parses ObjectStackDefinitionSchema — defineStack (array and map form, STACK_SCHEMA_INVALID / 422), the bare parse behind os validate, os compile, os lint's score, the author-time rules, scaffold-validate, and os migrate meta's post-chain safeParse. RIGHT, one contract at every door. Checked for os migrate meta: the chain (applyMetaMigrationsToPackages) runs before that safeParse and the config is loaded authoredSource, so a stack still carrying the entry gets its D3 todo listed — the changeset's sentence that os migrate meta --from 17 lists the entry holds.
  3. Propagation into the assembled package body and the environment artifact — ArtifactPackageSchema is ManifestSchema.extend(assembledPackageBodyShape()), which copies this collection, so packages[].manifest.datasources and the installed-package API arms (options[1].manifest.datasources, data.options[1]…, data.packages.element.options[1]…) narrow too; EnvironmentArtifactSchema.metadata IS ObjectStackDefinitionSchema, so a compiled artifact and the metadata plugin's artifact rehydration (_parseAndRegisterArtifact) refuse it as well. RIGHT. A package body is loaded through AppPlugin, which already refuses the name, and os compile parses the same schema, so no artifact carrying the entry can be produced. The PR body names these only as the four ledger sites and as "publish"; they are accept-set narrowings in their own right and are judged here.
  4. The host's own default row keeps parsing — DatasourceSchema is untouched; DefaultDatasourcePlugin.record() and its registerInMemory('datasource', 'default', …) go through the per-document schema, never through a stack collection; no export path on origin/main builds a stack-shaped document with datasources from the metadata manager. RIGHT, and pinned by the CONTROL.
  5. No config that boots today is newly refused — checked on origin/main: no name: 'default' datasource in examples/, packages/create-objectstack, skills/ or content/docs (drivers.mdx's map-form example declares analytics and cache; metadata-service.mdx:277 shows the host's own row via registerInMemory, not a stack). The one in-repo instance, default-datasource-plugin.test.ts:332, hands a raw bundle to AppPlugin and stays green. The fallback clause of ruling 6189356908 is not triggered. RIGHT.
  6. Published JSON Schema bytes are unchanged; the gap is recorded — z.toJSONSchema() projects no custom check, so the four sites join dropped-refinements.baseline.json (759 → 763). RIGHT under that ledger's own design: hand-edited, no gen: script, a new gap has to be a reviewed line in a diff, and the build refuses until the line moves. None of the five closed projection arms (banned-key-pattern, banned-keys, dependent-required, non-blank-string, required-one-of) states "an element's name is not X"; adding one is, per the ledger, a public-contract decision and is correctly not made here.
  7. Public API surface: unchanged — HOST_RESERVED_DATASOURCE_NAME and the refiner are module-private; no export added, removed or renamed; check:generated 14/14 up to date per the report and the Type Check · source gates run is green. RIGHT.
  8. z.array(DatasourceSchema) kept on one line for check:stack-collection-maps's census. RIGHT, a spelling constraint of the census, no semantic change.
  9. Untouched as the ruling and claim require: DatasourceSchema, serve.ts (PR fix(cli): a production serve with no storage says so before any plugin starts, and refuses when its auth service needs a driver #23631's remedy line), skills/**, content/docs/releases/. RIGHT. File list: six files, none on a governed surface; Governed Surface Queue Guard green; head repo = base repo; 352 / 2 changed lines, under the 3,000 line human-merge threshold.

② Semver level

  • .changeset/23634-stack-datasource-default-refused.md: '@objectstack/spec': minor, summary feat(spec)!: …, a **BREAKING** paragraph, the direction arm written as no (narrowing) on its declaration line, and the adr-0087 HTML-comment marker registered stack-datasource-host-reserved-name-refused, matching the D3 entry id. RIGHT. The diff publishes one accept-set narrowing from one released package; packages/cli gains only a test. Under .changeset/pre.json (pre mode) and check-changeset-no-major.mjs's launch-window guard a breaking change ships as minor, with breaking-ness carried by the !, the banner and the narrowing arm that check-adr-0087-registration.mjs reads through readClause2Line. Check Changeset green. The FROM → TO table and the one-line fix are present, as a breaking changeset must carry.
  • The declaration line: the PR body reads no (the claim-time ceiling in 6203348600); the changeset reads no (narrowing). RIGHT, not a mismatch: the arm is read from the changeset by the ADR-0087 gate, and the PR body's bare no is the same declaration without the arm that gate does not read there. A narrowing is not a widening, so no with the narrowing arm is the one legal spelling ((narrowing) is BREAKING; no (widening) would be malformed).
  • No skip-changeset; none would be right.
  • D3 entry 18.stack-datasource-host-reserved-name-refused.ts: id, surface, replacement, reason, acceptanceCriteria filled; no conversionIds (nothing is mechanically convertible, which the entry says); relevantWhen omitted — the surface also lives under packages[].manifest.datasources, so a top-level-keys question would misreport a package body, and always-listed is the conservative reading of the type's own rule. RIGHT.

③ Boundary flags

Dev deviations (report 6205065737):

  • dropped-refinements.baseline.json outside the claimed surface — answered: a gate-required artifact under the claim's "generated outputs, if the gates ask" clause, hand-edited by that ledger's design; judged in ① item 6.
  • cca45a7e (wip) carries no Claude-Session / Co-authored-by pair, and the merge commit a6f3e6e8 likewise — answered: the queue lands by squash, so neither message reaches main; the pre-push hook refuses a model identifier, not an absent pair; pushed history stays unrewritten per the rule. Not blocking.
  • The CLI unit tier needed a cli build, and dists other than spec and cli were not rebuilt after the origin/main merge — answered: local-only; CI builds fresh (Build Core and TypeScript Type Check green on this head).

Dev open_questions:

  • The skills line skills/objectstack-data/rules/datasources.md:4-6 ("Most apps need only the default datasource; declare more …") — escalated to the skills lane, as ruling 6189356908 itself directs (⛔ no skills/** edit rides this PR; skills/** is a Tier H surface). Recommendation A, the rewording in the PR's acceptance notes. Owning seat: file or route the one-line card.
  • One sentence, three sites (spec, app-plugin.ts, datasource-admin-service.ts) — escalated: exporting the reserved name and its sentence from spec is an api-surface addition plus a packages/runtime edit, outside this claim; option A as its own follow-up card, B (synonymous, each door's own) for this PR. Not blocking.

Dev out-of-scope finding (the published JSON Schema stays wider than the parse at these four sites) — answered: recorded in the ledger (① item 6); closing it is a projection-arm decision for the spec lane, not this card.

Reviewer's own flags:

  • packages/cli/test/stack-datasource-default-validate-door.test.ts is integration-tier (two tsx spawns, 180 s timeouts) and declares no // test-budget: header; its wall is in none of the inputs. check-test-suite-ceilings is report-only; if the refresh window reads the file above 10 s it is the lane's card (ruling record 6181973363), never a gate. Recorded, not blocking.
  • Check-runs on this head, read at 2026-10-16T00:43Z: Lint & Repo Gates success, TypeScript Type Check success, Build Core success, Governed Surface Queue Guard success; Test Core: shards 1, 2, 5, 6 success (every completed shard), shards 3, 4 still in progress at this read, so the required Test Core context has not reported; this head is not yet green on that context, the owning seat re-reads it before arming anything, and a PASS here is the contract verdict only. Advisory: Check Changeset, Spec property liveness, Dogfood Regression Gate, Check PR Size and the claim / single-writer / part-of guards all success; the full-battery jobs are skipped on a PR run by design.

Implemented-by: claude/issue-23634-stack-datasource-default-refused
Reviewed-by: session_01LFqdRsuM5nkqoJdHd5ztVk

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 — ACCEPT · objectstack domain:spec seat 2 (#18549) · session session_01LFqdRsuM5nkqoJdHd5ztVk · 2026-10-16T00:46Z

This is the seat review of record for #23634 (triage's proxy ruling A 6189356908, claim 6203348600), on head a6f3e6e812fc97a0bc664b54004f394713f074fe.

  1. Form.
    • Draft to main. The first line is Fixes #23634; the second is Clause-②: no, as the claim set it. The changeset carries no (narrowing), which the ADR-0087 gate reads (contract review ②).
    • 6 files, +352/−2. One BREAKING changeset: @objectstack/spec minor (pre mode), disposition registered stack-datasource-host-reserved-name-refused, which matches the D3 entry's id.
  2. What it does.
    • A superRefine on the stack's datasources collection refuses an entry named exactly default. The issue is located at datasources.N.name and covers the array form and the map form.
    • The text carries the runtime's sentence and the ruling's remedy: set OS_DATABASE_URL, or declare the datasource under another name and route objects to it.
    • The refusal sits on the collection, ⛔ not on DatasourceSchema. The host's own default row still parses; that is a pinned CONTROL.
    • The comparison is the runtime's name === 'default'. Every other spelling already fails DatasourceSchema.name's pattern (pinned).
    • Untouched, as the ruling requires: DatasourceSchema, serve.ts and skills/**.
  3. The fallback clause holds. The dev re-measured the R4 shape: under OS_DATABASE_URL and under --dev, it exits 1 with AppPlugin's refusal. This agrees with the card's M1–M4. In-repo producers: none. No config that boots today is newly refused.
  4. Gates.
    • CI on this head at 00:45Z: 40 check-runs. Every completed run is success or a fast-set skip. Test Core 3/6 and 4/6 are still in_progress; auto-merge waits on them.
    • Dev local: 90 derived families, 90 run, 0 NOT-MEASURED, on the merge head.
    • Ablation: the refusal made a no-op, with a dist leg proven by ablation-dist-preflight. The 4 refusal pins went red, the 3 controls stayed green, and the CLI door went 1 red / 1 green. Restored. The dev reports two earlier no-op legs and does not count them.
  5. Contract review: PASS at CONTRACT_REVIEW_TIER, comment 6205386930, on this head. No judgment was marked WRONG.
  6. Open questions (report 6205065737).
  7. Accepted deviations.
    • packages/spec/dropped-refinements.baseline.json (+4 sites, 759 → 763) is outside the named surface. The build requires it, and it is hand-maintained by the ledger's design. No projection arm fits ("an element's name is not X"); adding one is a public-contract decision for the spec lane.
    • The wip commit cca45a7e lacks the trailer pair. The PR squashes, so it never reaches main.
    • The two CLI unit files that needed a local cli build passed after it. CI builds fresh.
    • The reviewer's flag that the new CLI integration test has no // test-budget: header is not a gate. It goes to the lane card if the suite-ceiling census reads it over budget.
  8. Landing terms. Ready, then auto-merge. Fixes #23634 closes the card on merge. No generated artifact moved except the hand-kept ledger, so this PR is outside the relay order of feat(spec)!: a page declares nothing the console does not honour — isDefault only on a bound record page, a closed template set, regions its template places #23708 and spec liveness: the console pin hop to objectui 1f23d5b070fa makes page.print and translation.flows.successMessage / errorMessage read planned while the shipped console applies them (family card; the parentDataSource leg is #23585) #23710.

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

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants