From bec4fd6176fd2b8d7fedd2f834dae897b7cc48e0 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 15:50:55 +0000 Subject: [PATCH 1/2] docs(agents): state the app-vs-platform boundary once, so it stops being re-derived MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The boundary was decided ad hoc three times in one day, by three seats, each from scratch, and the three derivations differed. Nothing stated the rule. Written to fit the ratchet rather than raise it. AGENTS.md had exactly one line of headroom (1161 against a 1162 ceiling), and both funding routes an author may take alone are closed here: re-wrap funding is banned by the 2026-08-17 ruling, and a declared cross-file move cannot fund new content because the source decrease cancels against the destination raise. So the split follows the ratchet's own division of labour — principles in the instruction file, on-demand detail in references/: - AGENTS.md gains ONE line, a Context Routing row carrying the deciding question and pointing at the rule. 1161 -> 1162, exactly the ceiling, which is unchanged. - The rule itself lands in a new reference file: the deciding question, the publication test, and the two anti-patterns with the measurement behind each. - The new file is entered in both ratchet maps at its landed count, so it arrives metered rather than as an un-ceilinged file in a ratcheted directory. Every lesson is carried self-contained (failure mode, discipline, boundary) with no issue-ID citation, per the 2026-08-12 ruling that check:pm-skill-id-lint enforces. No deletions: nothing was removed to make room. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UHvF5hyiZjnCyExFnfQB8m --- .../references/app-platform-boundary.md | 66 +++++++++++++++++++ AGENTS.md | 1 + scripts/pm/check-skill-line-ratchet.mjs | 9 +++ 3 files changed, 76 insertions(+) create mode 100644 .claude/skills/pm-dispatch/references/app-platform-boundary.md diff --git a/.claude/skills/pm-dispatch/references/app-platform-boundary.md b/.claude/skills/pm-dispatch/references/app-platform-boundary.md new file mode 100644 index 0000000000..38d7271442 --- /dev/null +++ b/.claude/skills/pm-dispatch/references/app-platform-boundary.md @@ -0,0 +1,66 @@ +# App or platform — which work belongs where + +Routed here from `AGENTS.md`. This boundary was decided ad hoc three times in one day, by +three seats, each from scratch, and the three derivations differed. It is one rule, not new +policy: the rule those decisions already followed, written down so it stops being re-derived. + +Lessons here are self-contained by the 2026-08-12 ruling — failure mode, discipline and +boundary in the text, no issue number to dereference. + +## The deciding question + +**Could this be written by something that has only the metadata, and no knowledge of this +company?** + +| Answer | Belongs in | +|:--|:--| +| **No** — it encodes the company's own judgement | the **metadata app**: a discount ceiling, who a case is assigned to, how won/lost is booked; its own objects, views and flows | +| **Yes** — it only asks whether the metadata is self-consistent | the **platform**: reference integrity, translation coverage, view rosters, sharing-rule coverage, CRUD round-trips, RLS probes per declared position | +| the subject is platform behaviour, the cost lands on the app | the **platform**, and it is a **gap** until it does — asserting what a hook does inside the platform's own sandbox is one | + +The second row is the one that pays. An app that hand-writes it is hand-writing a +consistency proof the platform can derive once, for every app, and check on every build. + +## The second question, for a capability an app wants published + +**If a second app needed this, would it copy the implementation?** Yes ⇒ platform. + +One consumer is a **use**; two is a **contract**. A resolution wanted by four call sites +inside a single package stays package-private on exactly this test — the home question gets +decided when a second consumer is actually in hand, and a symbol that was never published +can move without a major. ⛔ The dependency graph is not the test: check the real edges +before claiming two packages cannot import each other, because three of six directed edges +usually already exist. + +⛔ This question decides what a package **in this monorepo** exports. It is contributor +guidance and must not ship to customers, who cannot act on it. + +## ⛔ Anti-pattern 1 — an app hand-copies a platform rule + +The copy diverges from the rule the build actually applies, so the app stays green while +the platform refuses. The divergence is invisible until the two disagree, and then the +app's own test suite is the thing arguing for the wrong answer. + +Measured twice. A card wanting the platform's hook-body lowering pass turned out to want +half a command that already ships (`os build --strict-body`, which turns "bundled instead +of lowered" into exit 1 — `packages/cli/src/commands/compile.ts`, of which `build` is an +alias) and half an `os lint` rule that exists nowhere in the tree. One level up, the same +shape ships a rule claiming "0 findings over the corpus" against a corpus that is not the +app it names. + +⇒ Before hand-writing against platform behaviour, grep the CLI for the command that already +does it, and confirm the rule you are citing exists. + +## ⛔ Anti-pattern 2 — a capability that under-delivers silently + +Worse than none. `os verify` once derived **zero** cases on a multi-package app and reported +success. `packages/cli/src/commands/verify.ts` states the shape in its own header: *a +verifier that under-verifies reports success it never established*. + +An app that meets one of these writes its own harness and never comes back — so the loss is +permanent, and it is invisible, because the capability is still reporting green. + +**The order is therefore fixed: make the derived half trustworthy first, then take the +hand-written half back.** Reversing it just deletes protection. The fix for the `os verify` +case landed as an ordered pair — ledger the four losses first so the fix has something to be +checked against, then close them — which is this rule executed. diff --git a/AGENTS.md b/AGENTS.md index 64fe9b5dbc..69ab2e85d6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -833,6 +833,7 @@ their output is current; the wrapper reports that each run rather than staying s | `apps/docs/**` | Docs Engineer | Fumadocs + Next.js, MDX | | `examples/**` | Example Author | Minimal, runnable, uses `defineStack` | | `content/docs/**` | Technical Writer | Respect auto-gen boundaries | +| `examples/**` app metadata vs `packages/**` capability | Scope Triage | Could this be written from the metadata alone, with no knowledge of this company? No ⇒ the app; yes ⇒ the platform, and a **gap** until it does. Full rule, publication test and both anti-patterns: `.claude/skills/pm-dispatch/references/app-platform-boundary.md` | | `../objectui/**` (sibling repo) | Studio UI Engineer | React + Shadcn + Tailwind, dark mode default | --- diff --git a/scripts/pm/check-skill-line-ratchet.mjs b/scripts/pm/check-skill-line-ratchet.mjs index c4ce620b5d..62340b4d93 100644 --- a/scripts/pm/check-skill-line-ratchet.mjs +++ b/scripts/pm/check-skill-line-ratchet.mjs @@ -599,6 +599,14 @@ export const CEILINGS = new Map([ // over-120B lines are all `table` rows, structurally exempt and NOT // re-wrappable; they are metered instead by MAX_TABLE_ROW_BYTES below. ['.claude/skills/pm-dispatch/references/compile-surfaces.md', 26], + // The app-vs-platform boundary — the deciding question (could this be written + // from the metadata alone, with no knowledge of this company?), the publication + // test (one consumer is a use, two is a contract) and the two anti-patterns. + // It is on-demand DETAIL by this header's own division of labour: AGENTS.md + // carries the one routing row, this file carries the rule and the measurement + // behind each clause. A NEW file, so this is not a raise and funds nothing — + // set at the landed line count, headroom 0, same convention as above. + ['.claude/skills/pm-dispatch/references/app-platform-boundary.md', 66], // Lane job descriptions (maintainer ruling 2026-08-19: per-lane PM job // descriptions move from seat-post prose into versioned skill references). // Set at landed line counts (headroom 0, same convention as above). @@ -916,6 +924,7 @@ export const MAX_TABLE_ROW_BYTES = new Map([ ['.claude/skills/pm-dispatch/references/seat-post-protocol.md', 0], ['.claude/skills/pm-dispatch/references/true-green.md', 0], ['.claude/skills/pm-dispatch/references/compile-surfaces.md', 352], + ['.claude/skills/pm-dispatch/references/app-platform-boundary.md', 219], ['.claude/skills/pm-dispatch/references/lanes/engine.md', 0], ['.claude/skills/pm-dispatch/references/lanes/services.md', 0], ['.claude/skills/pm-dispatch/references/lanes/cli.md', 0], From 088d652fb015baa2b9a5c01a995054fe2f291450 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 6 Sep 2026 11:37:57 +0000 Subject: [PATCH 2/2] docs(agents): re-pay the boundary pointer against the new 1058 ceiling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rules-only rewrite compressed AGENTS.md 1161 -> 1058 and locked the ceiling to 1058, which took back the single line of headroom this pointer was paid from. Git merged the two changes without a conflict, so nothing textual flagged it: the file came out at 1059 against a 1058 ceiling and would only have gone red in the merge queue, after being queued and kicked. Re-paid at net ZERO lines rather than by raising the ceiling: the standalone Scope Triage row is folded into the `examples/**` row that was already there. The Example Author constraint is preserved verbatim, and the boundary question is worded "on any tree" so folding it onto that row does not scope the rule to that directory. ⛔ The ceiling is untouched, ⛔ no line was reclaimed by re-wrapping (banned), and no existing rule was dropped: AGENTS.md is +1/-1 against main. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UHvF5hyiZjnCyExFnfQB8m --- AGENTS.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c7fadf39cc..60bc7266f5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -758,9 +758,8 @@ their output is current; the wrapper reports that each run rather than staying s | `packages/adapters/**` | Integration Engineer | Framework bindings, zero business logic | | `packages/client*/**` | SDK Engineer | Public API, DX, type safety | | `apps/docs/**` | Docs Engineer | Fumadocs + Next.js, MDX | -| `examples/**` | Example Author | Minimal, runnable, uses `defineStack` | +| `examples/**` | Example Author | Minimal, runnable, uses `defineStack`. App or platform, on any tree: could this be written from the metadata alone, with no knowledge of this company? No ⇒ the app; yes ⇒ the platform, and a **gap** until it does — `.claude/skills/pm-dispatch/references/app-platform-boundary.md` | | `content/docs/**` | Technical Writer | Respect auto-gen boundaries | -| `examples/**` app metadata vs `packages/**` capability | Scope Triage | Could this be written from the metadata alone, with no knowledge of this company? No ⇒ the app; yes ⇒ the platform, and a **gap** until it does. Full rule, publication test and both anti-patterns: `.claude/skills/pm-dispatch/references/app-platform-boundary.md` | | `../objectui/**` (sibling repo) | Studio UI Engineer | React + Shadcn + Tailwind, dark mode default | ---