Skip to content

Commit c1602ba

Browse files
docs: shared picklist — NORTH-STAR feature line, two planned records-forms items, coverage waiver; upgrade skill stops claiming a connector sync ran (#20938)
Part of #19518 The Tier H docs follow-up for the shared picklist: the section "Also with this card" on the card, plus one skills correction carried from #20281 under ruling 5916259513. The card stays open until this lands; the seat closes it by hand. Five files, no package source, nothing published from any package. ## What changes 1. **`docs/NORTH-STAR.md`**: one new line in step ② 「本地跑起来、看到」, records axis, with the card's text verbatim: `records · 共享选项集:多个对象复用同一份选项,客户按组织追加值而不改应用源码 · records-forms · picklist-shared-across-objects, picklist-org-append`. It sits after the last existing records row (search), so it ranks below every records capability that already exists. Moving it up is a priority call for the maintainer. 2. **`docs/qa/platform-checklist/areas/records-forms.json`**: two new items, both `status: "planned"`, `since: null`, `P2`, with no steps (README, "Implementation status"): - `records-forms.picklist-shared-across-objects` covers phase 1. One picklist on two objects: both are served the same resolved options, a package `picklistExtensions` value reaches both, the write door refuses a value outside the set, and a locale switch relabels the options. It carries three drafted acceptance clauses taken from the design of record and from the acceptance sentence of #19519, so the runtime PR has a concrete target when it promotes the item. - `records-forms.picklist-org-append` covers phase 2. Organization A appends a value without editing the app source, A can write it, and organization B is refused. It carries no acceptance clauses, because the overlay's schema and write door are not designed. A `knownGaps` entry records that the overlay is not declared: the picklist registry row has `allowOrgOverride: false`, and ruling 5904864936 defers the overlay to its own sub-issue. 3. **`docs/qa/platform-checklist/coverage.json`**: `picklist` gets a waiver. It was UNCLASSIFIED. The waiver states the measured reason: `packages/spec/liveness/picklist.json` grades every key `planned`, and `check-stack-collection-maps` holds `picklists` and `picklistExtensions` as PENDING rows for #19519. It names both planned ids and states what retires the waiver. 4. **`skills/objectstack-upgrade/references/examples-upgrade.md`**: the worked R1 walks a protocol-16 `connectors[].fieldMappings[].transform` source. Since #20903 (0efbdc3), the chain removes the whole of `fieldMappings` after `transform` (`connector-sync-keys-removed`, toMajor 18, part of the default `os migrate meta --from 16` terminus), and the author meets that key's tombstone. Three sentences said otherwise, and each now says what is true: - "the parse error is the prescription" pointed at a `transform` tombstone. It now names the `fieldMappings` tombstone. - "the connector has been landing raw values for as long as it has been running" is now "no connector sync ever ran, so no value ever passed through this mapping". - The report template's "connector sync run against staging, 200 rows" is now "nothing ever read the key, so no data changes". - The report template's chain line now reads 16 → 18, the chain the default run replays. 5. **`scripts/check-platform-checklist.mjs`**: four sentences (three comments and the self-test success line) said the live ledger carries zero planned items. They now say that no `coverage.json` entry maps a planned item. ## How behaviour that does not exist yet is represented The checklist's own rules settle the status. `planned` is for a capability the definition requires and the platform does not implement or verify yet. A planned item never runs and is never coverage. Both items are planned: no runtime reads a picklist yet. The coverage rule also settles the waiver. A kind whose only items are planned is UNMAPPED, and the gate's instruction is "Add an item that RUNS, or waive the kind with a reason". An entry cannot hold both, so the planned ids are named in the waiver's prose. One consequence: `checklist-select capability:picklist` matches nothing until the waiver flips to `items`. Two content choices are the rules' to leave open. The dev report asks about both: - **Locale relabel:** it is on the phase-1 item. The card's grouping put it with the organization item, but promotion is per item and needs a passing run, and the acceptance sentence of #19519 groups the relabel with phase 1. - **Organization append:** it is a planned requirement, as the card's NORTH-STAR text says. Ruling 5904864936 defers the overlay until a customer requirement measures it, so the maintainer may prefer to trim the line's second half and drop the item. ## Skill readings (`skills/**`) | reading | before (aaad682) | after (cab0e1b) | | --- | --- | --- | | `examples-upgrade.md` lines | 119 | 119 | | `examples-upgrade.md` bytes / tokens (ceil(bytes/4), ceiling 1197) | 4788 / 1197 | 4751 / 1188 | | package: all 10 `SKILL.md`, lines | 4394 | 4394 | | token ratchet, authored total | 143461 | 143452 | The additions are paid for by deleted content. Nothing was re-wrapped to buy lines. The ceiling stays 1197, with headroom 9. Lowering it is a separate edit to the ratchet script. ## Local verification, at cab0e1b - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derives 43 families from the five paths. All 43 ran; `--ran` answers "43 derived famil(ies) accounted for — 43 run, 0 NOT-MEASURED", exit 0. - 42 families exit 0. `check:platform-checklist` exits 1 on the pre-existing anchor below. - `check:pm-dispatch-gates` exits 0 (1976 self-test cases). - `check:doc-formula-expressions` first exited 3 (PREREQUISITE NOT MET: no lint or formula dist). After a locked build of those two packages it exits 0. - `pnpm check:platform-checklist` exits 1, the same as on `origin/main`: - before, 2 problems: `coverage.json · picklist: UNCLASSIFIED` and `areas/identity-auth.json: ABSENT SYMBOL … auth-plugin.ts#twoFactor`; - after, 1 problem: the same `twoFactor` anchor, which this diff does not touch; - the 5 anchors this diff adds resolve. - `pnpm gen:checklist-status` lists records-forms as 40 active and 2 planned (264 active and 2 planned overall). - `node scripts/check-skills-token-ratchet.mjs` exits 0: "examples-upgrade.md is 1188 tokens (ceiling 1197; headroom 9)". - `node scripts/pm/check-governed-merges.mjs --branch HEAD` exits 3, GOVERNED, landing tier H. Two of five paths hit the register (`skills/**`, `docs/NORTH-STAR.md`). The diff is +119 / -17. - No changeset: the diff releases nothing, because `skills/` ships through `npx skills add` and appears in no package's `files[]`. `skip-changeset` is applied. ## Acceptance notes - `scripts/check-platform-checklist.mjs` said in four places that the live ledger carries zero planned items. This PR corrects them (`cab0e1b557`); the tests are unaffected (221 assertions pass). Carrier: this PR. - `skills/objectstack-upgrade/evals/protocol-major-upgrade.json`, eval 3, was read and left unchanged. Its prompt, that the chain dropped `fieldMappings[].transform`, is still true. Its expected answer, "delete because the member never executed", is also still true, and it claims no sync run. - The card cites "ADR-0136 D2.4". No ADR-0136 exists in this repository's registry (zero hits), so neither item cites it; the design-of-record comment is cited instead. - The `Path:` lines on #19518 and #19519 still read 清单项待写. They can name the two ids once this lands. ## 维护者速读(草稿) 终稿以席位在本 PR 上的「维护者速读(终稿)」评论为准;下面与终稿一致。 **改了什么**: - 北极星「路上的功能点」第②步 records 组末尾,加一行「共享选项集」,文字照卡片原文。 - 平台测试清单加两个 `planned` 项(已计划,还不能跑): - 第一期:一份选项集两个对象共用、包扩展加值、越界值被拒、切语言换标签; - 第二期:组织自己追加值、本组织能写、别的组织被拒。 - `coverage.json` 给 picklist 记一条带理由的豁免。 - upgrade 技能示例改正三句话,报告模板里的迁移链改成 16 → 18。第一句原本把处方指向一个已不存在的 `transform` 墓碑;第二、三句说连接器同步跑过、落过数据,实际从来没有引擎跑过。 - 清单门禁脚本里有四处说「清单里没有 planned 项」(注释和自测提示语),改成与现状相符。 **为什么改**: - 卡片要求这一行和两个清单项随一个小文档 PR 落地。 - 规格层已合入,运行时(#19519)还没做,两个项都没东西可跑。清单规则规定这种情况标 `planned`、覆盖表用豁免,不能拿 `planned` 冒充覆盖。 - 技能文字错一句就是产品缺陷(北极星优先级第 4 条)。 **风险与代价(含回滚)**: - 只改了文档、一份对外技能参考文件,以及一个门禁脚本的注释和自测提示语;不动任何包,不发版。技能文件仍是 119 行,token 从 1197 降到 1188。 - 回滚就是 revert 三个提交。回滚后,清单门禁会重新报 picklist UNCLASSIFIED。 - 两个版本都过了档位审查(`8e7420019e` 和 `cab0e1b557`),CI 全绿。 **席位意见**: - 建议合并。 - 下面三处您合并时可以顺手改: ① 「客户按组织追加值」(第二期)现在就写进路线图和清单,作为 planned 需求。席位意见是保留:文字是卡片原文,planned 不算覆盖,也不宣称已经做到。但裁决 5904864936 说的是等有实测的客户需求再立子卡。您若不想现在就把它放上路,删掉那一行的后半句和第二个清单项即可。 ② 「切语言换标签」放在第一期的项里(卡片原来归在第二期)。它在第一期就会交付,放在第二期要等很久才能验证。席位同意这样放。 ③ 新行放在 records 组最后,排序最低。这张表的顺序就是优先级,您想提前就挪一下位置。 **你要做的**: - 合并本 PR。这是 Tier H,需要您亲手合并;或者给出授权的 APPROVED,由席位落地。 --- _Generated by [Claude Code](https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent f6ccca4 commit c1602ba

5 files changed

Lines changed: 119 additions & 17 deletions

File tree

‎docs/NORTH-STAR.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
- records · 写入规则在服务端兑现,撞车响亮、后悔得了、改过什么看得见 · records-forms · validation-rule-type-matrix, object-hook-lifecycle, delete-behavior-matrix, concurrent-edit-conflict, record-edit-undo, field-history-tracking
4141
- records · 记录上的动作与协作:按钮在声明的位置、参数契约在派发时兑现、讨论与 @ 提醒 · records-forms · action-location-matrix, action-param-widgets, upload-guard-blocks-confirm, record-discussion-mentions
4242
- records · 搜得到:跨字段、字段限定、权限一致、拼音、即时新鲜;全局搜索与命令面板同一条路 · search · cross-field-object-search, field-scoped-narrowing, rls-both-personas, pinyin-flag-both-sides, freshness-and-empty, console-global-search, command-palette-navigation
43+
- records · 共享选项集:多个对象复用同一份选项,客户按组织追加值而不改应用源码 · records-forms · picklist-shared-across-objects, picklist-org-append
4344
- access · 行级与字段级权限两边都对:受限成员只看自己的,该只读的只读、该看不见的不回给前端 · access-security · rls-both-sides, scope-depth-asymmetry, fls-mask-and-strip
4445
- access · 增删改查逐格兑现,改完权限立刻换脸,自查接口与服务端一致 · access-security · crud-permission-matrix, permission-matrix-edit-loop, me-permissions-aggregation-parity
4546
- access · 写入路径挡得住:只读剥离、伪造与转移 owner 被拒、默认可见度只能收紧 · access-security · write-path-guards, owd-save-gate

‎docs/qa/platform-checklist/areas/records-forms.json‎

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4183,6 +4183,104 @@
41834183
"ref": "claude/issue-18682-predicate-relationship-traversal"
41844184
}
41854185
]
4186+
},
4187+
{
4188+
"id": "records-forms.picklist-shared-across-objects",
4189+
"title": "One shared picklist on two objects: both are served the same resolved options, a package extension adds to both, a value outside the set is refused, and a locale switch relabels",
4190+
"since": null,
4191+
"status": "planned",
4192+
"revision": 1,
4193+
"priority": "P2",
4194+
"surface": "mixed",
4195+
"personas": [
4196+
"seeded admin (admin@objectos.ai / admin123)"
4197+
],
4198+
"fixtures": {
4199+
"app": "showcase",
4200+
"requires": [
4201+
"a picklist declared once (`*.picklist.ts`) and referenced by `Field.select({ picklist })` on two showcase objects, a `picklistExtensions` entry that adds one value to it, and `picklists.NAME` labels for a second locale. Stock showcase has none of these, and nothing serves a picklist until the runtime layer (#19519) lands, so the fixture arrives with it"
4202+
]
4203+
},
4204+
"acceptance": [
4205+
{
4206+
"clause": "both objects' fields are served with the SAME resolved `options` (the picklist's own values plus the extension's) beside the `picklist` name, and each object's form offers exactly that set",
4207+
"oracle": "api",
4208+
"verify": "read both objects' field metadata: each field parses as packages/spec/src/data/picklist.zod.ts#PicklistServedFieldSchema and the two option lists are equal; then enumerate the options each object's form offers",
4209+
"evidence": "both served field bodies + both form option enumerations"
4210+
},
4211+
{
4212+
"clause": "the write door judges both objects against the resolved set: the extension's value persists on object A, and a value outside the set is refused on object B, the refusal naming the picklist",
4213+
"oracle": "api",
4214+
"verify": "POST the extension's value to object A and re-read the row; POST an out-of-set value to object B and capture the refusal envelope",
4215+
"evidence": "the re-read + the refusal envelope"
4216+
},
4217+
{
4218+
"clause": "switching the locale relabels the options on both objects from the picklist's `picklists.NAME.options.VALUE` translations, and the stored values do not change",
4219+
"oracle": "api",
4220+
"verify": "read both objects' field metadata under each locale; a picklist-bound field takes its option labels through packages/spec/src/system/i18n-resolver.ts#translateObject, and the values are identical across locales",
4221+
"evidence": "both served bodies under each locale"
4222+
}
4223+
],
4224+
"negative": [
4225+
"a picklist-bound field served WITHOUT `options` is a FAIL: renderers, the record validator and filter pickers read `options`, so they would have nothing to offer or judge",
4226+
"a value outside the set accepted with 200 on either object is a FAIL: hiding it in the form is a client courtesy, and the write door is the boundary",
4227+
"a field naming a picklist that does not exist, served as an empty option list, is a FAIL: it must be refused loudly at load, naming the field and the package",
4228+
"an extension that duplicates, replaces or removes a base value is a FAIL: extensions are additive only, and a duplicate is refused loudly rather than last-wins"
4229+
],
4230+
"source": [
4231+
"#18164 design of record (comment 5715762696), items 1 to 6: the kind, the field reference, server resolution, the additive package extension, the translation face, and the verification sentence that this item and records-forms.picklist-org-append split between them",
4232+
"#19518, the spec layer (landed as addbbf02ab): packages/spec/src/data/picklist.zod.ts#PicklistSchema",
4233+
"#19518, the extension shape: packages/spec/src/data/picklist.zod.ts#PicklistExtensionSchema",
4234+
"#19519, the runtime layer this item waits on (load before `object`, additive merge, resolve at serve time, write validation against the resolved set); its acceptance sentence is this item's three clauses"
4235+
],
4236+
"history": [
4237+
{
4238+
"revision": 1,
4239+
"date": "2026-09-30",
4240+
"change": "initial — planned. The spec layer landed (#19518), but no runtime reads a picklist yet (packages/spec/liveness/picklist.json grades every key `planned`), so there is nothing to drive. The acceptance clauses are drafted from the design of record and #19519's acceptance; steps arrive with #19519, in the edit that promotes this item after a run in which it passes. The locale relabel sits on this item rather than on the organization item because it is phase-1 behaviour, and a promotion needs the whole item to pass",
4241+
"ref": "#19518"
4242+
}
4243+
]
4244+
},
4245+
{
4246+
"id": "records-forms.picklist-org-append",
4247+
"title": "An organization appends a value to a shared picklist without changing the app source: that organization can write it, and another organization is refused",
4248+
"since": null,
4249+
"status": "planned",
4250+
"revision": 1,
4251+
"priority": "P2",
4252+
"surface": "mixed",
4253+
"personas": [
4254+
"an admin of organization A (appends the value, then writes it)",
4255+
"a member of organization B (the same app and picklist, without A's value)"
4256+
],
4257+
"fixtures": {
4258+
"app": "showcase",
4259+
"requires": [
4260+
"two organizations on one environment, each with its own signed-in persona, sharing one app whose object has a picklist-bound field"
4261+
],
4262+
"knownGaps": [
4263+
"NOT DECLARED: the per-organization overlay is phase 2 of the picklist design. The picklist row of packages/spec/src/kernel/metadata-plugin.zod.ts#DEFAULT_METADATA_TYPE_REGISTRY carries `allowOrgOverride: false`, the one opt-in ADR-0005 allows for an organization overlay, and ruling 5904864936 on #18164 defers the overlay to its own sub-issue, filed when a customer requirement measures it. Nothing here can run until that sub-issue lands the overlay. Package-level extension (phase 1) is records-forms.picklist-shared-across-objects"
4264+
]
4265+
},
4266+
"negative": [
4267+
"organization B writing the value that only organization A appended, accepted with 200, is a FAIL: the appended value belongs to A's overlay alone",
4268+
"an organization overlay that removes or renames a value the owning package declared is a FAIL: the overlay adds values only, and removal or rename stays with the owning package",
4269+
"an append that needed an edit to the app source or to the owning package does not demonstrate this item: the capability is appending without touching either"
4270+
],
4271+
"source": [
4272+
"#18164 design of record (comment 5715762696), item 4 (a per-organization overlay of the picklist shape, additive only, whose allowance was left to the maintainer as an open point) and item 6 (the verification sentence: org A adds a value, A writes it, B is refused)",
4273+
"#18164 ruling 5904864936 (the overlay stays deferred to its own sub-issue on a measured customer requirement) and #19518 Scope 5 (not declared in the spec layer)",
4274+
"docs/adr/0005-metadata-customization-overlay.md (`allowOrgOverride` on the type registry is the only org-overlay opt-in)"
4275+
],
4276+
"history": [
4277+
{
4278+
"revision": 1,
4279+
"date": "2026-09-30",
4280+
"change": "initial — planned. The docs/NORTH-STAR.md feature-map line names per-organization append as half of the shared-picklist feature, and the design of record's verification sentence names this behaviour, so the ledger holds it as a planned requirement rather than leaving it off. It carries no acceptance clauses: neither the overlay's schema nor its write door is designed yet, and a clause would be written against a design nobody has settled",
4281+
"ref": "#19518"
4282+
}
4283+
]
41864284
}
41874285
]
41884286
}

‎docs/qa/platform-checklist/coverage.json‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@
225225
"api-backend.api-methods-verb-gate"
226226
]
227227
},
228+
"picklist": {
229+
"waived": "WAIVED 2026-09-30 — there is no runtime behaviour to drive yet, measured rather than asserted. The spec layer landed (#19518, addbbf02ab): PicklistSchema, `Field.select({ picklist })`, PicklistServedFieldSchema, `picklistExtensions` and the `picklists.NAME` translation face. No runtime reads a picklist: packages/spec/liveness/picklist.json grades every key `planned`, and scripts/check-stack-collection-maps.mjs carries `picklists` and `picklistExtensions` as rows PENDING the runtime layer (#19519) in the METADATA_ARRAY_KEYS and ARTIFACT_FIELD_TO_TYPE registration maps. No code resolves a picklist onto a field or judges a write against one, so an item written to RUN today could only assert absence. The two items that will carry this kind are authored as `planned`: records-forms.picklist-shared-across-objects (phase 1: two objects, the package extension, the write door, the locale relabel) and records-forms.picklist-org-append (phase 2: the per-organization overlay, deferred by ruling 5904864936 on #18164). They are not listed under `items`, because a kind whose only items are planned is UNMAPPED and an entry cannot hold a waiver and items together. WHAT RETIRES THIS WAIVER — #19519 lands resolve-at-serve, the additive merge and write validation; records-forms.picklist-shared-across-objects gains its steps, runs, passes and is promoted to `active`, and the same edit replaces this waiver with `items` naming it, the planned organization item beside it. ⚠️ Re-audit it every sweep, as SWEEP.md asks of every waiver."
230+
},
228231
"position": {
229232
"items": [
230233
"access-security.scope-depth-asymmetry",

‎scripts/check-platform-checklist.mjs‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,7 @@ const SELF_TEST_BATTERIES = Object.freeze({
10561056
[BATTERY_SYMBOL_ANCHORS]: 42,
10571057
// New with the `planned` status. Set at its landed count (headroom 0, the
10581058
// convention every entry above uses). The load-bearing third of it is the
1059-
// coverage direction: the live ledger carries ZERO planned items today, so
1059+
// coverage direction: no coverage.json entry maps a planned item today, so
10601060
// nothing but these fixtures can tell a working ratchet rule from a deleted
10611061
// one — the unreferenced-recipe argument, applied to a rule whose subject
10621062
// population is empty on purpose rather than by luck.
@@ -2277,10 +2277,10 @@ function selfTestSymbolAnchors() {
22772277
* ratchet. If a planned item ever counted as coverage, "凡是有的能力, 都要测试"
22782278
* would become "凡是有的能力, 都要打算测试", and the ratchet would go green on
22792279
* a kind nothing runs against. So the ratchet direction is pinned BOTH ways,
2280-
* on fixtures, not on the tree: the live ledger carries zero planned items and
2281-
* is expected to for a while, which means the real data cannot tell "this rule
2282-
* works" from "this rule was deleted" — the same silent-success argument the
2283-
* unreferenced-recipe battery above makes.
2280+
* on fixtures, not on the tree: no coverage.json entry maps a planned item,
2281+
* which means the real data cannot tell "this rule works" from "this rule was
2282+
* deleted" — the same silent-success argument the unreferenced-recipe battery
2283+
* above makes.
22842284
*/
22852285
function selfTestPlannedStatus() {
22862286
const failures = [];
@@ -2538,7 +2538,7 @@ if (process.argv.slice(2).includes('--self-test')) {
25382538
' and the `/meta` call-spelling refusal reads its vocabulary out of the live generated contract, fires on every folded spelling a `call` can instruct, and stays silent on the canonical singular, on parameter placeholders, and on the `why`/`expect`/`source`/`requires` prose that narrates the fold;' +
25392539
' and the line-citation limb DETECTS NOTHING ITSELF EITHER: the last forked grammar in this file went into the shared core at #18592, so what is pinned here is the BINDING — the corpus declaring `pathlessLineCitations`, a source read finding no citation regex and no detector while the same read DOES find the declaration, the binding driven ON and OFF against ONE text so the green is the declaration working rather than a text that would have matched anyway, the DARK case that a citation both grammars already agreed on keeps its verdict either way, the refusal to over-fire on this ledger\'s own HTTP statuses, config literals, URL ports, clock times and quoted JSON, and the live zero with the control that says it is a reading;' +
25402540
' and the symbol-anchor limb DETECTS NOTHING AND RESOLVES NOTHING ITSELF: it is a registered corpus (#18107), so the grammar, the walk and the verdict are all `scripts/symbol-anchors.mjs`\'s, pinned here by a source read that finds no local extension set, no anchor regex and no detector while the same read DOES find the registration, by the anchorable-extension vocabulary being the shared OBJECT rather than a copy of it, by the `runs/` exclusion driven three ways on the live corpus (the subtree holds files, none is swept, the areas beside it still are, and dropping the exclusion puts them back), and by the #16898 binding re-taken through the registration — a call site / import / local parameter / string-substring all reading ABSENT, the positive control that a declaration and a complete quoted token still resolve, a `.json` key resolving where a `.json` value does not, an INLINE object-literal key reading absent where one at the start of a line resolves — with the closed, grow-never residual and the per-file anchor floor held in both directions beside it;' +
2541-
` and the \`planned\` status is driven on fixtures rather than on a ledger that carries none of it — the accept set widened without losing its closure, \`since: null\`/no-steps/personas relaxed for planned alone while the ${plannedStatus.liveItems} live items are judged exactly as before, and the coverage ratchet held BOTH ways: a planned item beside an active one is silent, a kind whose only items are planned is UNMAPPED, and the bearing set is pinned NOT to contain \`planned\`; and the two CALL SITES those rules ride on are pinned by a source read over comment-MASKED source driven ON and OFF, because severing either one — by deletion OR by commenting it out in place — left this very self-test green; \u26d4 that pin is a TEXT pin and G12 records the three semantic severings it cannot see.`,
2541+
` and the \`planned\` status is driven on fixtures — the accept set widened without losing its closure, \`since: null\`/no-steps/personas relaxed for planned alone while the ${plannedStatus.liveItems} live items are judged exactly as before, and the coverage ratchet held BOTH ways: a planned item beside an active one is silent, a kind whose only items are planned is UNMAPPED, and the bearing set is pinned NOT to contain \`planned\`; and the two CALL SITES those rules ride on are pinned by a source read over comment-MASKED source driven ON and OFF, because severing either one — by deletion OR by commenting it out in place — left this very self-test green; \u26d4 that pin is a TEXT pin and G12 records the three semantic severings it cannot see.`,
25422542
);
25432543
process.exit(0);
25442544
}
@@ -2619,7 +2619,7 @@ if (symbolAnchorControl.failures.length) {
26192619
}
26202620
// And for the `planned` status. Its schema half is exercised by the tree the
26212621
// moment anyone authors a planned item; its COVERAGE half is not, and will not
2622-
// be for as long as the ledger's planned count is the 0 this gate prints. A
2622+
// be while no coverage.json entry maps a planned item. A
26232623
// deleted ratchet rule and an honest ledger print the same green, so the
26242624
// fixtures below it are the only thing that can tell them apart.
26252625
const plannedStatusControl = selfTestPlannedStatus();

‎skills/objectstack-upgrade/references/examples-upgrade.md‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ The shape in a protocol-16 project:
1616
}
1717
```
1818

19-
The chain deletes the key (`field-mapping-transform-removed`) and the schema
20-
tombstones it, so the parse error *is* the prescription: the union had five
21-
members and **no runtime ever executed any of them**. The customer wrote it
22-
because they wanted a transformation, and that need is real regardless.
19+
The chain deletes the key (`field-mapping-transform-removed`), then all of
20+
`fieldMappings` (`connector-sync-keys-removed`), whose tombstone *is* the
21+
prescription: **nothing ever ran any connector field mapping**. The customer
22+
wrote it because they wanted a transformation, and that need is real regardless.
2323

2424
The prescription names one live target; the rest is the business decision:
2525

@@ -29,10 +29,10 @@ The prescription names one live target; the rest is the business decision:
2929
| multi-source, multi-stage transformation | **nothing** — the L2 ETL layer retired at 17, unexecuted. Do it where it runs: warehouse ELT, a `flow`, a job. |
3030
| nothing — the value was already correct | delete the key and record that the transformation never ran. |
3131

32-
That third row is frequently the truth: the member never executed, so the
33-
connector has been landing raw values for as long as it has been running.
34-
Whether the downstream data is wrong is a question only the owner can answer —
35-
exactly the kind of finding the report exists to surface.
32+
That third row is frequently the truth: no connector sync ever ran, so no value
33+
ever passed through this mapping. Where the data really comes from, and whether
34+
it is right, only the owner can answer — exactly the kind of finding the report
35+
exists to surface.
3636

3737
### 3.4 The report — the human half
3838

@@ -44,7 +44,7 @@ maintainer can read in five minutes and a year from now. Write
4444
# Protocol 16 → 17 upgrade — <project>
4545

4646
**Status:** complete | complete with N open decisions
47-
**Spec:** <installed @objectstack/spec version> · **Chain:** 16 → 17
47+
**Spec:** <installed @objectstack/spec version> · **Chain:** 16 → 18
4848
**Verified:** `os validate` green · `tsc --noEmit` green · replay-from-17 applies 0 mechanical changes
4949

5050
## 1 · Mechanical (applied by the chain)
@@ -64,7 +64,7 @@ _N sites, M conversions. Ported into sources from `os migrate meta --out`._
6464
- **Options:** import-mapping `transform` · ETL step · delete
6565
- **Decision:** delete — owner confirmed the values arrive pre-scaled.
6666
_Decided by: <who>, <date>._
67-
- **Verified:** `os validate` green; connector sync run against staging, 200 rows, values unchanged.
67+
- **Verified:** `os validate` green; nothing ever read the key, so no data changes.
6868

6969
## 3 · Open decisions
7070

0 commit comments

Comments
 (0)