instar 1.3.732 → 1.3.734

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +127 -2
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/ConversationRegistry.d.ts +95 -0
  5. package/dist/core/ConversationRegistry.d.ts.map +1 -1
  6. package/dist/core/ConversationRegistry.js +292 -5
  7. package/dist/core/ConversationRegistry.js.map +1 -1
  8. package/dist/core/SessionManager.d.ts +15 -0
  9. package/dist/core/SessionManager.d.ts.map +1 -1
  10. package/dist/core/SessionManager.js +28 -0
  11. package/dist/core/SessionManager.js.map +1 -1
  12. package/dist/core/conversationBindToken.d.ts +36 -0
  13. package/dist/core/conversationBindToken.d.ts.map +1 -0
  14. package/dist/core/conversationBindToken.js +144 -0
  15. package/dist/core/conversationBindToken.js.map +1 -0
  16. package/dist/core/conversationIdentity.d.ts +14 -0
  17. package/dist/core/conversationIdentity.d.ts.map +1 -1
  18. package/dist/core/conversationIdentity.js +19 -0
  19. package/dist/core/conversationIdentity.js.map +1 -1
  20. package/dist/core/deliverToConversation.d.ts +68 -25
  21. package/dist/core/deliverToConversation.d.ts.map +1 -1
  22. package/dist/core/deliverToConversation.js +300 -11
  23. package/dist/core/deliverToConversation.js.map +1 -1
  24. package/dist/core/slackRefreshBinding.d.ts +8 -10
  25. package/dist/core/slackRefreshBinding.d.ts.map +1 -1
  26. package/dist/core/slackRefreshBinding.js +8 -16
  27. package/dist/core/slackRefreshBinding.js.map +1 -1
  28. package/dist/core/types.d.ts +9 -0
  29. package/dist/core/types.d.ts.map +1 -1
  30. package/dist/core/types.js.map +1 -1
  31. package/dist/monitoring/CommitmentTracker.d.ts +64 -0
  32. package/dist/monitoring/CommitmentTracker.d.ts.map +1 -1
  33. package/dist/monitoring/CommitmentTracker.js +58 -0
  34. package/dist/monitoring/CommitmentTracker.js.map +1 -1
  35. package/dist/monitoring/PromiseBeacon.d.ts +70 -0
  36. package/dist/monitoring/PromiseBeacon.d.ts.map +1 -1
  37. package/dist/monitoring/PromiseBeacon.js +221 -24
  38. package/dist/monitoring/PromiseBeacon.js.map +1 -1
  39. package/dist/server/AgentServer.d.ts +9 -0
  40. package/dist/server/AgentServer.d.ts.map +1 -1
  41. package/dist/server/AgentServer.js +8 -0
  42. package/dist/server/AgentServer.js.map +1 -1
  43. package/dist/server/routes.d.ts +12 -0
  44. package/dist/server/routes.d.ts.map +1 -1
  45. package/dist/server/routes.js +128 -10
  46. package/dist/server/routes.js.map +1 -1
  47. package/package.json +1 -1
  48. package/src/data/builtin-manifest.json +48 -48
  49. package/upgrades/1.3.733.md +108 -0
  50. package/upgrades/1.3.734.md +30 -0
  51. package/upgrades/side-effects/durable-conversation-identity-increment2.md +88 -0
  52. package/upgrades/side-effects/three-constitutional-standards.md +129 -0
@@ -0,0 +1,88 @@
1
+ # Side-Effects Review — Durable Conversation Identity, Increment 2 (the proof consumer)
2
+
3
+ **Spec:** docs/specs/durable-conversation-identity.md (CONVERGED round 11 — 0 CRITICAL / 0 MAJOR; approved under the standing Session-A operator preapproval, topic 29836). **Parent:** Structure beats Willpower — a commitment made in a Slack thread must survive a server restart and land back in that exact thread, and that guarantee lives in durable machinery (registry WAL + funnel), never in a session remembering to re-post.
4
+
5
+ **This increment is §6.1 STEP 2 — the flagship proof consumer.** It delivers the Phase-1 live proof: *create a commitment from a Slack THREAD, restart the server, watch the beacon heartbeat land back in that thread.* It also lands the deferred §4 hash-copy consolidation (increment-1 §4 note), the §5.2/§5.0(a) funnel hardening that ships WITH the funnel's first consumer, and the §7 bind-token authority primitive.
6
+
7
+ **Delivery stays dev-gated.** The `id<0` funnel arm is dark-on-fleet / live-on-dev via `conversationIdentity.followThrough` (`enabled` OMITTED → the developmentAgent gate resolves it) with `dryRun: true` FIRST — exactly as spec §9 prescribes. The Telegram lane (`id>0`) is byte-identical when the flag is off; a beacon whose `deliverMessage` dep is unwired falls back to today's `sendMessage` path verbatim.
8
+
9
+ ## §6.1 steps this increment comprises
10
+
11
+ Per the spec's rollout section (§6.1), increment 2 = **step 2 (Commitments + PromiseBeacon, the proof consumer)** plus the two clauses the spec explicitly assigns to ship WITH this increment (they are unimplementable/unsafe without a live consumer):
12
+
13
+ - **§4 hash-copy consolidation** (deferred from increment 1's §4 note): the three legacy `-(Math.abs(hash)+1)` copies collapse onto `candidateIdForRoutingKey`. `slackRefreshBinding.slackRoutingKeySyntheticId` is now a re-export; the `routes.ts` build-heartbeat inline copy mints (get-or-create) through the registry with a shared-candidate fallback; the `server.ts:slackChannelToSyntheticId` closure delegates to the registry. The mint-idiom ratchet allowlist shrinks accordingly (a fourth copy is still a CI failure).
14
+ - **The §5.2 `/telegram/reply/:topicId` 400-on-negative** (a behavior change, spec-assigned to the funnel increment, not the foundation): a negative id is a minted conversation and is refused with a 400 the recovery policy already classifies terminal (4xx → escalate), so no negative-id relay row retries forever.
15
+ - **§5.0(a) E1 ambiguous-outcome idempotency guard** — durable `send-intent` / `ambiguous-send` / `send-retire` / `send-intent-resolved` WRITERS (the op enum + replay shipped in increment 1); retirement-based LOGICAL lane, WINDOW-based CONTENT-HASH lane (R3-M1/R7-M2), the R8-M1 lane-scoped boot conversion, the R5-M3 inter-store crash order.
16
+ - **§3.5.2 bind-pin overlay WRITERS** (`bind-pin` / `bind-release`) + the record-carried `boundTuple` delivery rule with the SHARED id↔tuple coherence check (`idWithinCoherenceBound`, one implementation — R4-M1/R5-M2/R6-M4).
17
+ - **§5.1 permanent-vs-transient classification** over `SlackApiError.slackError` (the pinned set `{is_archived, channel_not_found, not_in_channel}` + the L5 drift canary), reachability flip/auto-clear + flap dampening, mass-unreachable P17 aggregation, the fire()-re-arm-in-finally + N-fail dead-letter, and the R3-M16 non-owning STAND-DOWN + ownership-recheck.
18
+ - **§7 bind-time authority** — the stateless self-authenticating per-session bind token (R4-M3), minted at spawn into `INSTAR_BIND_TOKEN`, verified at `POST /commitments` on a minted id (fail-closed); the positive-id branch (R6-minor-4) + the R7-minor-2 straggler grace stamp.
19
+
20
+ The E1 `deterministicKind` gate-exempt arm (increment 3) and the §5.2 P17 per-conversation/global budgets (increment 5) are DEFERRED to their own §6.1 increments and are NOT in this increment — named explicitly, not silently.
21
+
22
+ ## What changed
23
+
24
+ 1. **conversationIdentity.ts** — new `idWithinCoherenceBound(id, tuple)`: the ONE shared §3.5 ingest ≡ §3.5.2 delivery coherence predicate (`cand − MAX_PROBE_DISTANCE ≤ id ≤ cand`). Increment-9 replication ingest MUST reuse this export (a second copy is a CI failure per §10).
25
+ 2. **conversationBindToken.ts (new)** — the §7 primitive: `ensureBindTokenSecret` (first-boot 32-byte secret, stateDir, EXCLUDED from the backup manifest — R5-minor-4), `mintBindToken`/`verifyBindToken` (HMAC-SHA256, base64url, stateless — validation reads the bootstrap set FROM the token so a live tmux session's token survives any number of server restarts), `ensureBindDeployStamp`/`bindDeployStampAgeDays` (the R7-minor-2 grace clock), and `createConversationBindAuth` (the ctx surface).
26
+ 3. **ConversationRegistry.ts** — increment-2 WRITERS: `bindPin`/`bindRelease`/`getBindPinTuple` (§3.5.2, fsynced); `recordSendIntent`/`recordLikelyPosted`/`retireSend`/`resolveSendIntent`/`isSendSuppressed`/`pruneSendGuardState` (§5.0(a), durable, lane-tagged); `convertOrphanedSendIntents` (R8-M1 lane-scoped boot conversion, staged post-compose + fsynced before serving — R9-low-3/R10-minor-1); `setReachability` (§5.1, idempotent, flap-tracked); `readIdForRoutingKey` (the §4 READ-ONLY comparison path — no get-or-create side-effect); and the durable-bind WAL-ensure on a pre-existing speculative entry (a durable bind is not re-derivable, so its journal line is fsynced NOW). `ambiguousSends` gains a `lane` tag (snapshot-complete). Health carries a `sendGuard` block.
27
+ 4. **deliverToConversation.ts** — the funnel grows the increment-2 arms: `classifySlackSendError` (§5.1); the §3.5.2 overlay (boundTuple → coherence-check → resolve(tuple), else the local pin, else resolve(id)) with the redirect/incoherent/pending typed outcomes; the E1 guard (lane select → suppress read → durable intent → post-outcome record/retire/resolve); reachability flip + mass-unreachable coalescing; new outcomes `already-delivered-recently` (DELIVERED-EQUIVALENT, R7-M1) and the `standDown`/`permanent` flags on `not-delivered`.
28
+ 5. **CommitmentTracker.ts** — `boundTuple` + `boundBy` fields on `Commitment`; a `ConversationBinder` dep (late-bound via `setConversationBinder`); `record()` performs the durable bind on a minted (negative) id (typed refusal THROWS — a minted-id commitment is never created silently unpinned) and denormalizes `boundTuple`; `mutateSync`/`applyMutationWithCAS` release the pin refcount on a TERMINAL transition (`delivered`/`withdrawn`/`expired` only — never verified/violated, which oscillate).
29
+ 6. **PromiseBeacon.ts** — the funnel swap: `deliverMessage`/`retireSend`/`ownsConversation`/`deadLetterAfterConsecutiveFailures` deps; `emitUserSend` routes every send through `deliverMessage` when wired (logicalSendId = `<commitmentId>:<sendSeq>`, boundTuple carried); `applyDeliveryOutcome` implements the R5-M3 seq-before-retire order, the R7-M1 delivered-equivalent seq advance, the §5.1 permanent dead-letter, the transient N-fail dead-letter, and the R3-M16 stand-down; fire() now re-arms in `finally` (a thrown send can never kill the timer); `saveHotState` is atomic tmp→rename (R4-minor-1); `recheckStandDowns` rides the existing external-block sweep (R4-minor-4, no new timer).
30
+ 7. **slackRefreshBinding.ts** — `slackRoutingKeySyntheticId` is now a re-export of `candidateIdForRoutingKey` (§4 copy retired).
31
+ 8. **routes.ts** — the `/telegram/reply` 400-on-negative; the build-heartbeat inline-hash retirement (registry mint + shared-candidate fallback); the §7 bind gate on `POST /commitments` (minted-id fail-closed, positive-id token-validated, token-less legacy fail-open + straggler attention); the §6.1 dark-window undeliverable-notice; typed binder refusals → 409; new ctx surfaces `conversationBindAuth` + `conversationFollowThrough`.
32
+ 9. **SessionManager.ts / types.ts** — `mintBindToken` on `SessionManagerConfig`; `bootstrapConversationIds` on both spawn option shapes; `bindTokenEnvFlags` injects `INSTAR_BIND_TOKEN` into both the headless and interactive tmux -e blocks (a failed mint never blocks a spawn).
33
+
34
+ ## Blast radius
35
+
36
+ - **Delivery cannot fire on the fleet.** The funnel's `id<0` arm is dev-gated + dryRun-first; PromiseBeacon's `deliverMessage` dep is only wired on the dev-gate live path (bootstrap wiring lands in the next commit of this increment). When unwired, the beacon uses today's `sendMessage` verbatim.
37
+ - **The §4 consolidation is value-identical by golden parity.** Every retired copy computed `-(Math.abs(sumShiftHash(key))+1)`; the registry records the same value. The read-only build-heartbeat callsite falls back to the shared candidate when the registry is degraded/absent (§3.6 fail-toward-delivery), so the PresenceProxy suppression signal is unchanged.
38
+ - **The 400-on-negative is the one intentional behavior change.** No live caller sends `reply/<negative>` today (Slack proxy sends route through `slack.sendToChannel`, not `/telegram/reply`); the 400 only bites a future mis-wired negative-id relay row, which the recovery policy already terminalizes.
39
+ - **The bind gate is inert until `conversationBindAuth` is wired.** With it unwired (this commit), `POST /commitments` behaves exactly as today. Once wired, minted-id binds are fail-closed and positive-id binds keep today's behavior unless the session carries a token.
40
+
41
+ ## Risk + mitigation
42
+
43
+ - **Risk:** a crash between the transport-accept and the E1 suppressor append double-posts the re-fire. **Mitigation:** the durable `send-intent` (fsynced BEFORE the send) + the R8-M1 lane-scoped boot conversion — a beacon-lane orphan converts to a suppressor, a content-hash orphan resolves toward retry.
44
+ - **Risk:** the seq resets on a torn hot-state write and re-collides against the durable E1 entry, silently suppressing a legitimate post-restart heartbeat. **Mitigation:** `saveHotState` is atomic tmp→rename (R4-minor-1).
45
+ - **Risk:** an ownership migration delivers via bare `resolve(id)` and reopens the C3-class misdelivery. **Mitigation:** the record-carried `boundTuple` + the shared coherence check; an incoherent pair is a typed non-delivery on NEITHER field (R6-M4).
46
+ - **Risk:** a confused session binds into a foreign conversation. **Mitigation:** the §7 bind token (fail-closed on minted ids); `boundBy` is recorded from the verified payload, never the request body.
47
+ - **Risk:** a mass bot-removal fans out N dead-letter attention items. **Mitigation:** the §5.1 emitter coalescing window (60s) → ONE summary item; flap dampening for archived↔unarchived bounce.
48
+
49
+ ## Migration parity
50
+
51
+ - No config-default change in THIS commit (the `followThrough`/`recording` blocks + backup manifest landed in increment 1). The §7 bind-token secret + deploy stamp are created at first boot by the runtime (not the migrator) and are deliberately NOT backup-manifest entries (R5-minor-4).
52
+ - `Commitment.boundTuple`/`boundBy` are additive optional fields — the CommitmentsSync receive clamp (`clampReplicatedRow`) type-clamps `boundTuple` (platform enum + channelId/threadTs regexes) on the wire; a legacy row without them falls back to `resolve(id)`.
53
+
54
+ ## Rollback
55
+
56
+ - Runtime: `conversationIdentity.followThrough` (dev-gate → off, or dryRun:true) reverts all `id<0` delivery. The §4 consolidation is rollback-by-revert (pure refactor). The 400-on-negative and the bind gate revert with the code.
57
+ - Rotating `INSTAR_BIND_TOKEN`'s secret (delete `state/conversation-bind-token.secret`, restart) is the revocation lever — a LOUD deliberate trade (live sessions hit the typed refusal until respawned), never a silent side effect.
58
+
59
+ ## Tests
60
+
61
+ - `tests/unit/conversation-identity.test.ts` — extended: the shared `idWithinCoherenceBound` predicate (candidate, within-bound offset, out-of-bound reject, positive/zero reject).
62
+ - `tests/unit/conversation-bind-token.test.ts` (new) — mint/verify round-trip, tampered-payload reject, wrong-secret reject, bootstrap-set membership, stateless survival across a fresh verifier (restart proxy), deploy-stamp grace.
63
+ - `tests/unit/conversation-registry.test.ts` — extended: bind-pin refcount (two binds/one close → holds; last close → released), reachability flip/idempotent/auto-clear/flap, the E1 durable send-guard (suppress read both lanes, the R8-M1 boot conversion both lanes, the missing-lane retry), the durable-bind WAL-ensure on a speculative entry, `readIdForRoutingKey` no-write.
64
+ - `tests/unit/deliver-to-conversation.test.ts` — extended: `classifySlackSendError` (the pinned set + canary + clean vs ambiguous), the E1 idempotency (ambiguous → single post, clean-transient → not suppressed, content-hash window split), the boundTuple overlay (redirect, incoherent refusal, pending), permanent → reachability flip + not-delivered permanent, dryRun still typed non-delivery.
65
+ - `tests/unit/promise-beacon-conversation-delivery.test.ts` (new) — the fire()-re-arm-in-finally on a thrown send, the R5-M3 seq-before-retire order, the R7-M1 un-mute (ambiguous → suppressed re-fire → next tick posts seq+1), the restart-double-post + restart-between-heartbeats shapes, the R3-M16 stand-down + recheck, the permanent dead-letter, dead-letter scoping (non-owning never counts), the funnel-unwired legacy passthrough.
66
+ - `tests/integration/conversation-registry-routes.test.ts` — extended: `/telegram/reply/<negative>` → 400; `POST /commitments` on a minted id with a valid/missing/tampered bind token; the dark-window undeliverable notice.
67
+ - `tests/e2e/conversation-registry-lifecycle.test.ts` — extended: the full inbound→mint→commitment(boundTuple)→restart→beacon→funnel→Slack-thread delivery cycle (the flagship Phase-1 proof), and the mint-idiom ratchet still green after the §4 consolidation.
68
+
69
+ ## Agent awareness
70
+
71
+ The GET /conversations* Capabilities entry shipped in increment 1 covers the read surface. No new agent-facing route in this increment (the funnel is internal; the bind token is spawn-env infrastructure the agent never touches directly). <!-- tracked: durable-conversation-identity -->
72
+
73
+ ## CONTINUATION NOTE (for the next session)
74
+
75
+ **Done (committed, typecheck-green):**
76
+ - **Core (commit 1):** the ConversationRegistry writers, the funnel §5 arms, CommitmentTracker binder + boundTuple + terminal release, PromiseBeacon funnel-swap machinery, the §4 consolidation (all three copies), the 400-on-negative, the §7 bind-token module + SessionManager env plumbing + the routes bind gate, the shared coherence predicate.
77
+ - **Bootstrap wiring (commit 2 — the ACTIVATION):** `createConversationDelivery` constructed in server.ts with the real Slack adapter send + `resolveDevAgentGate(followThrough)`; `conversationFollowThrough` + `conversationBindAuth` exposed on the AgentServer ctx; `commitmentTracker.setConversationBinder({ bind, release })` backed by `mintForDurableBinding` + `bindPin`/`bindRelease`; `mintBindToken` threaded into the SessionManager config (constructed early so the spawn env carries the token); `deliverMessage`/`retireSend`/`ownsConversation` threaded into the PromiseBeacon; the Slack spawn passes `bootstrapConversationIds: [conversationId]`; `ensureBindDeployStamp` on first boot; AgentServer default-constructs `conversationBindAuth` so the bind gate is alive on every init path. **The feature is now LIVE-on-dev (dryRun-first) / dark-on-fleet.**
78
+
79
+ **Remaining (tests + the §4 server.ts closure retirement — the next session's scope):**
80
+ 1. **The tests** (all three tiers, per the Tests section above): the new `conversation-bind-token.test.ts` + `promise-beacon-conversation-delivery.test.ts` unit suites; the registry/funnel unit extensions; the integration route tests (400-on-negative, bind-token gate, dark-window notice); the e2e restart-delivery cycle. **These are REQUIRED for a green CI** — the Testing Integrity Standard is non-negotiable. Author them against the pure surfaces first (funnel + registry + bind-token are all injectable/deterministic).
81
+ 2. **Existing-test sweep (behavior-change-breaks-old-tests):** the `/telegram/reply` 400-on-negative and the PromiseBeacon `fire()` re-arm/`emitUserSend` return-type changes may break existing beacon/route tests — sweep `tests/**` for `reply/-` negative-id posts and beacon send assertions BEFORE pushing (sharded CI surfaces them one slow round at a time otherwise).
82
+ 3. **The §4 read-shaped callsites in server.ts** — the `slackChannelToSyntheticId` closure still exists; several comparison callsites (`=== topicId`) use it. Retire it to `conversationRegistry.readIdForRoutingKey` (READ path — added this increment, no write side-effect) / `idForSessionKey` (mint path), keeping the `slackProxyChannelMap` reverse lookups working (a staged migration keeps both until the reverse map is fully removed). Follow-up commit — the mint-idiom ratchet allowlist still lists `commands/server.ts` so the closure is allowed until retired.
83
+
84
+ The clean seam: commit 1 is additive/inert; commit 2 activates it dev-gated. The Telegram lane is byte-identical when `followThrough` is off; a beacon whose `deliverMessage` dep is present but whose id resolves `id>0` still takes today's Telegram path.
85
+
86
+ ## Post-push landing note (ratchet baseline)
87
+
88
+ CI's `no-silent-fallbacks` ratchet flagged two catch blocks in `conversationBindToken.ts` (the verify-MAC decode → null, and the deploy-stamp age reader → null). Both are intentional fail-CLOSED / observability paths, not degraded-service fallbacks — tagged `@silent-fallback-ok` with their safe-direction justification so the ratchet holds at its 491 baseline with zero untagged swallows from this increment.
@@ -0,0 +1,129 @@
1
+ # Side-Effects Review — Three Operator-Ratified Constitutional Standards (multi-machine-always, self-heal-before-notify, alerts-topic-routing)
2
+
3
+ **Version / slug:** `three-constitutional-standards`
4
+ **Date:** `2026-07-03`
5
+ **Author:** Echo (autonomous)
6
+ **Second-pass reviewer:** not-required (Tier 1; documentation-only, no runtime surface, no decision point)
7
+
8
+ ## Summary of the change
9
+
10
+ This change lands the TEXT of three operator-ratified constitutional standards into the constitution and nothing else. It touches two documentation files and adds this review's gate artifacts:
11
+
12
+ - `docs/STANDARDS-REGISTRY.md` — three new enforceable standard entries: (A) *An Instar Agent Is Always a Multi-Machine Entity*, (B) *Self-Heal Before Notify — The Operator Hears Only When Self-Healing Fails*, (C) *Notices Route to the Alerts Topic, Never a New One*.
13
+ - `docs/INSTAR-DESIGN-PRINCIPLES-AND-LESSONS.md` — matching lessons-catalog entries P21, P22, P23, and the reviewer-loop range bump (P1-P20 → P1-P23).
14
+ - `docs/specs/three-constitutional-standards.eli16.md` — the plain-English ELI16 overview (this Tier-1 gate artifact).
15
+ - `upgrades/side-effects/three-constitutional-standards.md` — this artifact.
16
+ - `upgrades/next/three-constitutional-standards.md` — the release-note fragment.
17
+
18
+ No `src/`, `scripts/`, hook, skill, job, template, or route is touched. This is constitution text. The three standards each name an enforcement build in their "Applied through" section, but that build is a separate, tracked follow-up — it is NOT part of this change. There is no runtime surface here and no decision point is added, modified, removed, or shadowed.
19
+
20
+ ## Decision-point inventory
21
+
22
+ This change has NO decision-point surface. It adds no gate, sentinel, watchdog, hook, or route; it blocks, allows, filters, or routes nothing at runtime. The standard texts describe decision points that a FUTURE enforcement build will implement, but this change lands only the prose. The rest of this section is intentionally empty per the template's "state that explicitly and skip the rest" instruction.
23
+
24
+ ---
25
+
26
+ ## 1. Over-block
27
+
28
+ **What legitimate inputs does this change reject that it shouldn't?**
29
+
30
+ No block/allow surface — over-block not applicable. This change adds no runtime code path; it rejects nothing at runtime. The one "gate" it interacts with is the spec-converge lessons-aware reviewer, which merely READS these entries and asks their questions of future specs — a reasoning input, not an automated blocker.
31
+
32
+ ---
33
+
34
+ ## 2. Under-block
35
+
36
+ **What failure modes does this still miss?**
37
+
38
+ No block/allow surface — under-block not applicable. There is no runtime check to miss a failure mode. The honest limitation is that landing the TEXT does not by itself enforce the standards: until the tracked enforcement build ships, a spec could still ship an undefended machine-local surface, a first-notify watcher, or a per-alert topic without an automated block. That gap is explicitly named in each standard's "Applied through" section and is out of scope for this text-only change.
39
+
40
+ ---
41
+
42
+ ## 3. Level-of-abstraction fit
43
+
44
+ **Is this at the right layer?**
45
+
46
+ Correct layer. These are constitutional standards, so the constitution (`docs/STANDARDS-REGISTRY.md`) and the lessons catalog (`docs/INSTAR-DESIGN-PRINCIPLES-AND-LESSONS.md`) are exactly where they belong — they are the documents the spec-converge lessons-aware reviewer already loads. Standard (A) is deliberately placed as a sibling of the existing *Cross-Machine Coherence* standard and explicitly distinguishes itself from it (coherence governs lease/seamlessness robustness; (A) governs the default posture of every new feature). Standard (C) is placed as the routing corollary of the existing *Bounded Notification Surface* (P17). No lower-level primitive is re-implemented; each entry points at the machinery that already exists and names the enforcement build that will make it load-bearing.
47
+
48
+ ---
49
+
50
+ ## 4. Signal vs authority compliance
51
+
52
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
53
+
54
+ **Does this change hold blocking authority with brittle logic?**
55
+
56
+ - [x] No — this change has no block/allow surface.
57
+
58
+ This is documentation text. It holds no authority of any kind at runtime — it neither blocks, delays, nor rewrites any message or action. Notably, standard (B) *Self-Heal Before Notify* itself encodes the signal-vs-authority spirit for the future enforcement build (a watcher self-heals first and escalates only on exhaustion), but landing (B)'s text adds no authority now.
59
+
60
+ ---
61
+
62
+ ## 5. Interactions
63
+
64
+ **Does this interact with existing checks, recovery paths, or infrastructure?**
65
+
66
+ - **Shadowing:** none. No runtime check runs before or after another. The only consumer of these entries is the spec-converge lessons-aware reviewer, which reads them additively.
67
+ - **Double-fire:** none. No event is acted on.
68
+ - **Races:** none. No shared runtime state is written.
69
+ - **Feedback loops:** none at runtime. The one intended "loop" is intellectual: these entries feed the spec-converge reviewer, which asks their questions of future specs — the designed effect, not an uncontrolled feedback path.
70
+ - **Document-level composition:** (A) is explicitly reconciled against *Cross-Machine Coherence*; (B) is explicitly composed with *No Silent Degradation* and *Near-Silent Notifications*; (C) is explicitly composed with *Bounded Notification Surface (P17)*. Each entry names the relationship rather than silently overlapping, so no reader is left to guess which standard governs.
71
+
72
+ ---
73
+
74
+ ## 6. External surfaces
75
+
76
+ **Does this change anything visible outside the immediate code path?**
77
+
78
+ - Other agents on the same machine? No.
79
+ - Other users of the install base? Only as documentation — instar agents read the constitution; these entries become visible to the spec-converge lessons-aware reviewer on the next spec review. No behavior changes.
80
+ - External systems (Telegram, Slack, GitHub, Cloudflare)? No.
81
+ - Persistent state (databases, ledgers, memory files)? No — no state file is written or migrated.
82
+ - Timing or runtime conditions? No.
83
+ - **Operator surface (Mobile-Complete Operator Actions):** No operator-facing action is added or touched. This change adds no dashboard form, no approval page, no grant/revoke/secret-drop surface, and no PIN-gated or approval-class route. Not applicable.
84
+
85
+ No external surface changes.
86
+
87
+ ---
88
+
89
+ ## 6b. Operator-surface quality (Operator-Surface Quality standard)
90
+
91
+ No operator surface — not applicable. This change touches no dashboard renderer/markup file, no approval page, and no grant/revoke/secret-drop form. It is documentation only.
92
+
93
+ ---
94
+
95
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
96
+
97
+ **When this agent runs on MORE THAN ONE machine, what is this feature's posture?**
98
+
99
+ **replicated** — these are constitution DOCS. The documentation tree (`docs/`) is versioned in git and ships to every machine through the same git-based distribution as all instar source; there is no per-machine copy and no per-machine divergence. A standard written once is identical on every machine by construction.
100
+
101
+ Notably, this is the FIRST change authored *under* standard (A), and it satisfies (A) by default: its posture is unified (git-replicated), not machine-local, and it names the replication path (git). It emits no user-facing notice (nothing to one-voice-gate), holds no durable runtime state (nothing to strand on topic transfer), and generates no URLs (nothing to survive a machine boundary).
102
+
103
+ ---
104
+
105
+ ## 8. Rollback cost
106
+
107
+ **If this turns out wrong in production, what's the back-out?**
108
+
109
+ Trivial. Pure documentation change — revert the commit (or ship a follow-up docs edit) and it is gone. No persistent state to clean up, no data migration, no agent state to repair, and no user-visible runtime regression during the rollback window. The only "cost" of a wrong standard is that the spec-converge lessons-aware reviewer would cite a bad rule until the revert lands — caught at spec-review time, not in a live incident.
110
+
111
+ ---
112
+
113
+ ## Conclusion
114
+
115
+ This review confirms the change is documentation-only with no runtime surface, no decision point, and no external side effect. It lands the three operator-ratified standard texts (registry + lessons entries) and their Tier-1 gate artifacts. Multi-machine posture is unified-by-git-replication — and this change is itself the first artifact authored under the standard (A) it introduces, satisfying it by default. Rollback is a one-commit revert. The change is clear to ship. The enforcement build that mechanically polices each standard is a tracked, separate follow-up and is explicitly out of scope here.
116
+
117
+ ---
118
+
119
+ ## Second-pass review (if required)
120
+
121
+ Not required. Tier 1, documentation-only, no block/allow surface, no session-lifecycle or gate/sentinel/watchdog/guard code — the Phase-5 high-risk triggers do not apply to a text-only constitution change.
122
+
123
+ ---
124
+
125
+ ## Evidence pointers
126
+
127
+ - `git show --stat 11923c4c4` — the two docs files already committed (STANDARDS-REGISTRY + lessons catalog), 84 insertions.
128
+ - `docs/specs/three-constitutional-standards.eli16.md` — plain-English overview, 6.8k chars (well over the 800-char floor), first-heading body ~1k chars before the first subheading.
129
+ - `node scripts/pre-push-gate.js` — release-note fragment validates and the fresh side-effects artifact is present (this file).