You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 709c90b
Browse filesBrowse the repository at this point in the historyBrowse files
docs(agents): Prime Directive #14 governs three surfaces, and names the register
PD#14 scoped itself to `docs/adr/**` while the required check it describes
(`ADR maintainer approval`) has held `.claude/skills/**` since 2026-08-17 and
`skills/**` since 2026-08-18. Two more sites in the same file enumerated the
same one prefix: the §7 arming exclusion and the Post-Task Checklist exception.
- PD#14 becomes a class rule ("a governed surface"), carries both maintainer
rulings verbatim with dates, names the three prefixes ONCE, and points at
`GOVERNED_SURFACES` in scripts/check-adr-merge-approval.mjs as the register —
with the runnable one-liner that prints today's set.
- §7 and the Post-Task Checklist stop enumerating and defer to #14, so the
next surface addition edits one place, not four.
- Corrects the same paragraph's stale description of the enforcement: the gate
reads state, not actors (maintainer ruling 2026-08-12), and CODEOWNERS routes
only `docs/adr/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
11. **Worktree-first — never edit on the shared `main` checkout.** This repo is edited by **multiple agents at once**; the shared tree has its HEAD switched and reset *under you*, silently clobbering uncommitted work — a feature branch on the *shared* checkout is **not** enough (it still gets switched under you). Before your **first file edit**, be in a dedicated worktree on a feature branch: `git worktree add ../objectstack-<task> -b <branch> main && cd ../objectstack-<task> && pnpm install`. Two PreToolUse hooks **enforce** this — `.claude/hooks/guard-main-checkout.sh` blocks `Edit`/`Write`/`NotebookEdit`, and `.claude/hooks/guard-main-checkout-bash.sh` blocks the identical write arriving through **Bash** (`>`/`>>` redirection, `sed -i`, `perl -i`, `tee`, `cp`, `mv`, `rm`, `touch`) — and both check the **target file's own repo**, so sibling repos (`objectui`/`cloud`) you touch are covered too (deliberate non-task override: `OS_ALLOW_MAIN_EDITS=1`, one switch for both). The Bash guard is precision-first: it never blocks reads, and any shape it cannot resolve with confidence (`bash -c …`, `xargs`, `node -e`, a `$VAR`/glob target) is allowed through — the rule still outranks the hook. **The one thing a worktree does *not* isolate is the stash** — `refs/stash` lives in the **common** `.git`, shared by every worktree; a third hook (`guard-shared-stash.sh`, `OS_ALLOW_STASH=1`) blocks the mutating forms, and the collision-free replacements are in Multi-agent discipline below.
174
174
12. **Contract-first — fix the metadata, not the runtime.** This is a metadata-driven framework: `packages/spec` is the one contract between metadata *producers* and the runtime/renderers that *consume* it. When a piece of metadata "doesn't work," ask **first**: *is it spec-compliant? is this the long-term-correct direction?* If the metadata is wrong, fix it at the **producer** and **reject it at authoring/publish** (validation / lint) so the error surfaces loudly — do **not** add a lenient alias or `??` fallback in a consumer (a node executor, the REST layer, a renderer) to tolerate off-spec input. A tolerant fallback fossilizes the wrong convention into a second de-facto contract, dilutes the spec, and hides the producer's bug — one strict contract beats N dialects. This is an **internal** contract (we own both ends), so "be liberal in what you accept" (Postel) does **not** apply — that's for untrusted boundaries. Change the **spec** only when the spec itself is genuinely wrong, and then deliberately (edit the Zod schema + migrate), never by accreting consumer-side fallbacks. When an alias must be tolerated at all, declare it as an **ADR-0087 conversion-layer entry** (never a bare `??`, and no executor shims) so it is declared, loud, tested, and *removable on a schedule* — the `cfg.filter ?? cfg.filters`-style fallbacks the flow executors once carried were all paid down exactly that way, emptying and deleting the executor shim that read them. Stored `sys_metadata` rows (data at rest) are covered from the other side: every rehydration seam replays the **full** conversion chain — retired entries included — via `applyConversionsToStoredItem` (ADR-0087 addendum), so a consumer never needs its own accommodation for a legacy stored shape either. *Worked example:* an AI-authored flow node used wrong key names and template syntax for what the executor reads → the fix was correcting the authoring skill + a publish-gate lint that rejects the wrong shape, **not** a runtime alias in the executor (that alias was proposed and rejected). Strengthens #5.
175
175
13. **An accepted ADR binds until a superseding ADR says otherwise.** Reversing a recorded decision is itself a decision: it needs a **new ADR** (or an amended status line on the old one), not a changeset that quietly does the opposite. Before changing behaviour in `docs/adr/`-governed territory, **grep the ADRs for the surface you are touching** — the decision is often older and broader than the code comment in front of you. A reversal of three accepted ADRs once landed as a patch-level changeset and held for a day; the mechanism was not carelessness — **the file being edited never named the ADRs that governed it**, so the author could not have known. Hence the corollary: when you implement an ADR's decision, **leave its id in the code**, and anchor load-bearing spots in `scripts/adr-anchors/` (`pnpm check:adr-anchors`) — **one new JSON file per anchor, named for the path it anchors; there is no index to register it in** — so the next author is told which decision they are standing on. A decision nobody can find is a decision that will be reversed.
176
-
14.**⛔ An ADR is confirmed and merged by the maintainer, by hand — no AI seat merges, queues, or arms auto-merge on a `docs/adr/**` PR.**Maintainer ruling, 2026-08-08, verbatim and untranslated:
176
+
14.**⛔ A governed surface is confirmed and merged by the maintainer, by hand — no AI seat merges, queues, or arms auto-merge on a PR whose diff touches one.**Two maintainer rulings, verbatim and untranslated; the second extends the first from ADRs to skills:
177
177
178
-
> **adr 只能由维护者自己确认,人工合并,ai 不得擅自合并。**
178
+
> **adr 只能由维护者自己确认,人工合并,ai 不得擅自合并。** (2026-08-08)
179
179
180
-
**Authoring stays open to every seat** — drafting an ADR, pushing the branch, opening the PR, revising it under review. What is reserved is the **landing**: on any PR whose diff touches `docs/adr/**`, ⛔ never merge it, ⛔ never add it to the merge queue, ⛔ never call `enable_pr_auto_merge`. Judge it on the PR's **file list**, not on its description, and a **mixed diff is not a proportion question** — one path hit is enough; if the rest needs to land, split the ADR into its own PR. **Reviewed + approved + fully green does not override this.** Under #13 an accepted ADR *is* the decision, so merging one is the act of adopting a governance position — the one class of change about which "CI is green" carries no information at all (a thorough, fully-green ADR draft has been closed by the maintainer on demand grounds no gate could evaluate). **Already armed or queued when you read this?** ⚠️ Converting the PR back to **draft** is the only action that reliably removes it from the merge queue; `disable_pr_auto_merge` alone drops the arming but **not** queue membership. Do both, then confirm from the remote that it is in neither the queue nor `origin/main` (§7's draft-flip re-arm note, run backwards). ⚠️ **And do not read draft as a barrier that holds by itself** — a drafted ADR PR has nevertheless been merged, twice, by two different AI seats within one hour of the ruling above (both ratified retroactively, explicitly setting no precedent). The barrier is machine enforcement — `docs/adr/` in CODEOWNERS plus a required check that stays red unless the maintainer's own account has approved; this directive is the part that binds the seat reading it. A seat that has read this far is not thereby licensed to judge an exception; the rule has no exception to judge.
180
+
> **所有 skills 的更新和 adr 类似,需要人工审核** (2026-08-11)
181
+
182
+
**Which surfaces — and where that list actually lives.** Three prefixes as of 2026-08-18: `docs/adr/**`; `.claude/skills/**`, the repo-internal playbooks every *later* dispatch reads; and `skills/**`, the catalog published outward to customer projects. ⚠️ **Read that as a reading aid, not as the register.** The set grew twice in two days and no gate ties this prose to it, so a stale count here is a silent, permanent lie. The register is the `GOVERNED_SURFACES` table in `scripts/check-adr-merge-approval.mjs`, reported by the required check **`ADR maintainer approval`** — print today's set rather than trusting the sentence above, and add a fourth surface *there*, never here: `node -e "import('./scripts/check-adr-merge-approval.mjs').then(m=>console.log(m.governedGlobs().join(' · ')))"`
183
+
184
+
**Authoring stays open to every seat** — drafting the ADR or the skill, pushing the branch, opening the PR, revising it under review. What is reserved is the **landing**: on any PR whose diff touches a governed prefix, ⛔ never merge it, ⛔ never add it to the merge queue, ⛔ never call `enable_pr_auto_merge`. Judge it on the PR's **file list**, not on its description, and a **mixed diff is not a proportion question** — one path hit is enough; if the rest needs to land, split the governed files into their own PR. **Reviewed + approved + fully green does not override this.** Under #13 an accepted ADR *is* the decision, so merging one is the act of adopting a governance position — the one class of change about which "CI is green" carries no information at all (a thorough, fully-green ADR draft has been closed by the maintainer on demand grounds no gate could evaluate). The two skill prefixes are reserved for a different reason of the same shape: the internal playbooks are the operating protocol every later dispatch reads, and the published catalog lands in codebases this repo cannot see, so a bad merge propagates into work nobody has started yet — and green says nothing about whether it should propagate.
185
+
186
+
**Already armed or queued when you read this?** ⚠️ Converting the PR back to **draft** is the only action that reliably removes it from the merge queue; `disable_pr_auto_merge` alone drops the arming but **not** queue membership. Do both, then confirm from the remote that it is in neither the queue nor `origin/main` (§7's draft-flip re-arm note, run backwards). ⚠️ **And do not read draft as a barrier that holds by itself** — a drafted ADR PR has nevertheless been merged, twice, by two different AI seats within one hour of the first ruling above (both ratified retroactively, explicitly setting no precedent), and a skill PR whose own body said it was awaiting a human merge was flipped ready by an unidentified seat and landed by the merge queue with zero reviews of any kind. **The barrier is machine enforcement — and know exactly how much of it there is.** The required check `ADR maintainer approval` covers all three prefixes and stays red while the PR has no standing APPROVED review **or** has auto-merge armed (armed is red whether or not it is approved: auto-merge is a standing instruction to merge later, and the unattended merge happens in the armed-and-approved state). What it does **not** do is read actors, by ruling — maintainer, 2026-08-12, verbatim: 「不要指定具体的人」. Any account with review rights satisfies it, an AI seat included, and no check can see who presses merge. CODEOWNERS routes `docs/adr/` to the maintainer and **nothing routes the two skill prefixes**, so on those the automatic request for his review does not exist either. The machine narrows the hole; this directive is the part that closes it. A seat that has read this far is not thereby licensed to judge an exception; the rule has no exception to judge.
181
187
182
188
15.**⛔ A version release is performed by the maintainer, by hand — no AI seat publishes, tags, cuts a Release, or triggers a release workflow, and none merges the Version Packages PR.** Maintainer ruling, 2026-08-07, verbatim and untranslated:
183
189
@@ -325,9 +331,11 @@ Even inside your own worktree, operate defensively:
325
331
**arm only what is already green and accepted.**
326
332
327
333
⛔ **Two classes of PR never enter this path, however green:** (a) a diff touching
328
-
`docs/adr/**` (**Prime Directive #14**); (b) the **Version Packages** PR, or any PR
329
-
whose merge performs a release (**Prime Directive #15**). Read the PR's file list
330
-
(`get_files`) **and its author** before you arm anything.
334
+
any **governed surface** (**Prime Directive #14**, which names them and holds the
335
+
current list) — that list is longer than `docs/adr/**` and has grown twice in two
336
+
days, so re-read it rather than recalling it; (b) the **Version Packages** PR, or any PR whose merge performs
337
+
a release (**Prime Directive #15**). Read the PR's file list (`get_files`) **and
338
+
its author** before you arm anything.
331
339
332
340
**Green means the gate-carrying jobs' `conclusion` is `success`** — not "no failure
333
341
yet"; `in_progress` is not a pass. Arming a red PR does not queue it, it hides it:
@@ -908,10 +916,10 @@ new open registry? Add it to `OPEN_CAPABILITY_REGISTRIES` in the same PR that fi
908
916
a feature branch, commit, push, open a PR, and — once remote CI is fully green and
909
917
the PR is accepted — arm auto-merge so the queue lands it (Multi-agent discipline
910
918
§7: never straight to `main`; never arm a PR that isn't green yet). A finished task
911
-
= a merged PR, not a dirty working tree. ⛔ **Except a diff touching `docs/adr/**`**:
912
-
push it, open the PR, and stop there — landing it is the maintainer's, by hand
913
-
(Prime Directive #14). For that one class, a finished task = a PR left visibly
914
-
awaiting a human merge.
919
+
= a merged PR, not a dirty working tree. ⛔ **Except a diff touching a governed
920
+
surface** (Prime Directive #14 names them — more than ADRs): push it, open the PR,
921
+
and stop there, landing it is the maintainer's, by hand. For that class, a
922
+
finished task = a PR left visibly awaiting a human merge.
915
923
3.**Add a changeset for feature work.** When the change is a feature or functional improvement, run `pnpm changeset` (or add a `.changeset/*.md` entry) describing it before committing. Pure bug fixes do **not** require a changeset.
916
924
**Breaking changesets must carry their migration.** If the change removes or renames anything an author can write (a spec key, an export, a config field), the changeset body must state the FROM → TO mapping and the one-line fix — this text ships to consumers as `CHANGELOG.md` inside the npm package and is what an upgrading agent greps after the tombstone error. Removing an authorable spec key also requires a tombstone so the rejection itself carries the prescription — `retiredKey()` (`packages/spec/src/shared/retired-key.ts`) on a non-strict schema, or an entry in the relevant `UNKNOWN_KEY_GUIDANCE` / `*_RETIRED_KEY_GUIDANCE` map (see `object.zod.ts`, `ai/tool.zod.ts`) when the schema is `.strict()`. The changeset is one of fourteen surfaces a retirement touches — follow the `spec-property-retirement` skill (`.claude/skills/`) rather than reconstructing the kit, and note the two routes imply **opposite** liveness-ledger dispositions.
917
925
**A breaking changeset must also state its ADR-0087 disposition, in writing.** Add exactly one marker to the changeset body — `pnpm check:adr-0087-registration` enforces it, and the CI step is *Require an ADR-0087 disposition on a declared-breaking changeset*:
0 commit comments