instar 1.2.81 → 1.2.83
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.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +110 -11
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +23 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +7 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +59 -75
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts +43 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +123 -24
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/types.d.ts +26 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/SessionReaper.d.ts +153 -0
- package/dist/monitoring/SessionReaper.d.ts.map +1 -0
- package/dist/monitoring/SessionReaper.js +376 -0
- package/dist/monitoring/SessionReaper.js.map +1 -0
- package/dist/monitoring/TokenLedger.d.ts +12 -0
- package/dist/monitoring/TokenLedger.d.ts.map +1 -1
- package/dist/monitoring/TokenLedger.js +22 -0
- package/dist/monitoring/TokenLedger.js.map +1 -1
- package/dist/monitoring/transcriptProber.d.ts +44 -0
- package/dist/monitoring/transcriptProber.d.ts.map +1 -0
- package/dist/monitoring/transcriptProber.js +57 -0
- package/dist/monitoring/transcriptProber.js.map +1 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +21 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +3 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +1 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +3 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +108 -0
- package/dist/server/routes.js.map +1 -1
- package/dist/threadline/CollaborationSurfacer.d.ts +67 -18
- package/dist/threadline/CollaborationSurfacer.d.ts.map +1 -1
- package/dist/threadline/CollaborationSurfacer.js +132 -37
- package/dist/threadline/CollaborationSurfacer.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +63 -63
- package/src/scaffold/templates.ts +21 -0
- package/upgrades/1.2.81.md +13 -0
- package/upgrades/1.2.82.md +26 -0
- package/upgrades/1.2.83.md +26 -0
- package/upgrades/side-effects/1.2.81.md +127 -0
- package/upgrades/side-effects/session-reaper.md +42 -0
- package/upgrades/side-effects/threadline-notification-routing.md +46 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Upgrade Guide — NEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: minor -->
|
|
4
|
+
<!-- Valid values: patch, minor, major -->
|
|
5
|
+
<!-- patch = bug fixes, refactors, test additions, doc updates -->
|
|
6
|
+
<!-- minor = new features, new APIs, new capabilities (backwards-compatible) -->
|
|
7
|
+
<!-- major = breaking changes to existing APIs or behavior -->
|
|
8
|
+
|
|
9
|
+
## What Changed
|
|
10
|
+
|
|
11
|
+
**SessionReaper — pressure-aware cleanup of idle-but-alive sessions.** A new monitor that reaps sessions sitting idle at a ready prompt (holding memory) — but ONLY when the machine is under memory pressure, and it NEVER reaps a session that might be working. It requires *positive* proof of idleness (turn complete + at a ready prompt + screen byte-static across several checks + no running process + no transcript growth) and KEEPs on any ambiguity. Ships **OFF + dry-run by default** — the only monitor that kills on a heuristic, so it stays dark until an operator validates the dry-run log and opts in. Closes the gap behind the 2026-05-25 fleet pileup (idle sessions accumulated until the machine starved and cross-agent messaging silently failed because agents could no longer spawn).
|
|
12
|
+
|
|
13
|
+
New read-only endpoint `GET /sessions/reaper` shows the live pressure tier and, per session, the verdict + the exact gate that kept it. `SessionManager` gains a single-writer `terminateSession()` so the existing idle-kill and the reaper can never double-kill. The zombie-kill recovery veto now also defers to the socket + silence sentinels.
|
|
14
|
+
|
|
15
|
+
## What to Tell Your User
|
|
16
|
+
|
|
17
|
+
- **Idle sessions get cleaned up under memory pressure — safely.** When your machine fills up with idle agent sessions, this sweeps them so new sessions (and incoming cross-agent messages) don't get refused. It will never reap a session that's actually working. It's off by default; ask me to turn it on after we watch its dry-run log.
|
|
18
|
+
- **You won't notice anything unless you enable it.** No behavior change on update.
|
|
19
|
+
|
|
20
|
+
## Summary of New Capabilities
|
|
21
|
+
|
|
22
|
+
| Capability | How to Use |
|
|
23
|
+
|-----------|-----------|
|
|
24
|
+
| SessionReaper (idle-session cleanup under pressure) | `monitoring.sessionReaper.enabled:true` (leave `dryRun:true` first). Off by default. |
|
|
25
|
+
| Reaper observability | `GET /sessions/reaper` — pressure tier + per-session verdict + keptBy |
|
|
26
|
+
| Single-writer session termination | `SessionManager.terminateSession()` — idle-kill + reaper share one CAS kill path |
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Side-Effects Review — Free-text guard template resolution
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `free-text-guard-template-resolution`
|
|
4
|
+
**Date:** `2026-05-25`
|
|
5
|
+
**Author:** `instar-codey`
|
|
6
|
+
**Second-pass reviewer:** `Echo`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
This change fixes how the post-update migrator finds built-in template files.
|
|
11
|
+
The free-text guard hook was looking only in the compiled-template layout, but
|
|
12
|
+
published packages ship that hook in the source-template layout. The migrator
|
|
13
|
+
now has one shared template loader for hook, script, and playbook templates. The
|
|
14
|
+
free-text guard and existing similar readers use that loader.
|
|
15
|
+
|
|
16
|
+
## Decision-point inventory
|
|
17
|
+
|
|
18
|
+
This change does not add, remove, or modify any block/allow decision. It changes
|
|
19
|
+
file lookup for a static built-in template. The free-text guard's own behavior is
|
|
20
|
+
passed through unchanged.
|
|
21
|
+
|
|
22
|
+
- `free-text-guard.sh` — pass-through — installed from the packaged template;
|
|
23
|
+
its blocking rules are not changed.
|
|
24
|
+
- `PostUpdateMigrator` template lookup — modified — uses shared lookup instead
|
|
25
|
+
of repeated one-off path checks.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 1. Over-block
|
|
30
|
+
|
|
31
|
+
No new block/allow surface. Over-block is not applicable to the template lookup
|
|
32
|
+
change. The free-text guard may still block exactly the same inputs it blocked
|
|
33
|
+
before; this patch only controls whether the hook is installed.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. Under-block
|
|
38
|
+
|
|
39
|
+
This does not change what the guard detects. The remaining failure mode is
|
|
40
|
+
package-shape drift: if a future publish stops shipping both supported template
|
|
41
|
+
locations, the migrator will fail clearly for the required free-text guard and
|
|
42
|
+
skip or fall back for callers that already had softer behavior. The new package
|
|
43
|
+
shape test covers the current published layout.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 3. Level-of-abstraction fit
|
|
48
|
+
|
|
49
|
+
The fix belongs in the migrator because the bug is runtime asset resolution, not
|
|
50
|
+
guard policy. A shared helper is the right layer: it keeps package-layout
|
|
51
|
+
knowledge in one place and lets each caller decide whether a missing template is
|
|
52
|
+
fatal, skippable, or eligible for fallback.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 4. Signal vs authority compliance
|
|
57
|
+
|
|
58
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
59
|
+
|
|
60
|
+
- [x] No — this change has no block/allow surface.
|
|
61
|
+
|
|
62
|
+
The change does not introduce a detector or an authority. It does not decide
|
|
63
|
+
whether a message, action, or user request should be blocked. It only locates a
|
|
64
|
+
static template file so existing migration behavior can install the existing
|
|
65
|
+
hook.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 5. Interactions
|
|
70
|
+
|
|
71
|
+
- **Shadowing:** no existing decision path is shadowed. Existing template
|
|
72
|
+
readers now share one lookup helper.
|
|
73
|
+
- **Double-fire:** no duplicate installer is added. The same migration still
|
|
74
|
+
writes the same built-in hook once.
|
|
75
|
+
- **Races:** no shared persistent state is added. File reads are local and
|
|
76
|
+
synchronous, matching the previous migrator style.
|
|
77
|
+
- **Feedback loops:** none. The resolver does not feed runtime decisions or
|
|
78
|
+
telemetry.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 6. External surfaces
|
|
83
|
+
|
|
84
|
+
Existing agents see the external effect on update: the free-text guard hook can
|
|
85
|
+
be installed again from the package we actually publish. The hook content and
|
|
86
|
+
runtime behavior do not change. There is no new user-facing command, API,
|
|
87
|
+
database field, or external service dependency.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 7. Rollback cost
|
|
92
|
+
|
|
93
|
+
Rollback is a normal patch revert. The change writes no new persistent state and
|
|
94
|
+
does not migrate data. If it misbehaves, revert the shared resolver refactor and
|
|
95
|
+
ship a patch. Agents that already received the hook do not need repair because
|
|
96
|
+
the hook content itself is unchanged.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Conclusion
|
|
101
|
+
|
|
102
|
+
The review found no new decision authority and no new block/allow behavior. The
|
|
103
|
+
main production risk is future package layout drift, and the integration test now
|
|
104
|
+
checks the packed package shape and runs the compiled migrator from that shape.
|
|
105
|
+
This is clear to ship once CI is green.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Second-pass review (if required)
|
|
110
|
+
|
|
111
|
+
**Reviewer:** Echo
|
|
112
|
+
**Independent read of the artifact: concur**
|
|
113
|
+
|
|
114
|
+
Echo reviewed the diagnosis and implementation direction during the Threadline
|
|
115
|
+
collaboration and signed off on the root cause, shared resolver approach, and
|
|
116
|
+
packed-package smoke as the evidence needed before normal CI.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Evidence pointers
|
|
121
|
+
|
|
122
|
+
- Approved spec: `docs/specs/FREE-TEXT-GUARD-TEMPLATE-RESOLUTION-SPEC.md`
|
|
123
|
+
- Plain-language spec overview:
|
|
124
|
+
`docs/specs/FREE-TEXT-GUARD-TEMPLATE-RESOLUTION-SPEC.eli16.md`
|
|
125
|
+
- Focused tests: 6 passing.
|
|
126
|
+
- Build and lint: passing.
|
|
127
|
+
- Packed package smoke: passing.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Side-Effects Review — SessionReaper
|
|
2
|
+
|
|
3
|
+
Spec: `docs/specs/SESSION-REAPER-SPEC.md` (v2 CONVERGED + ratified). Build branch `build/session-reaper`.
|
|
4
|
+
|
|
5
|
+
## What changes for a deployed agent
|
|
6
|
+
|
|
7
|
+
- A new monitor (`SessionReaper`) is constructed and started at server boot. **Default OFF + dry-run** (`monitoring.sessionReaper.enabled:false, dryRun:true`), so deployed agents get **no behavior change** until an operator opts in. New config block arrives via the standard `ConfigDefaults`/`applyDefaults` migration; operator-set values are never overwritten.
|
|
8
|
+
- New read-only endpoint `GET /sessions/reaper` (503 when unwired, 200 snapshot otherwise).
|
|
9
|
+
- `SessionManager` gains `terminateSession()` (single-writer CAS), `isRelayLeaseActive()`, and `markReaping/clearReaping/isReaping`. The existing idle-kill now funnels through `terminateSession` and skips reaping-leased sessions; `killSession` shares the CAS guard and now sets `endedReason` (its event emissions are unchanged — still no `sessionComplete`).
|
|
10
|
+
- The zombie-kill recovery veto (`activeRecoveryChecker`) is recomposed to include the socket + silence sentinels (previously compaction + rate-limit only) — a strict superset; nothing is dropped.
|
|
11
|
+
|
|
12
|
+
## Over/under-block analysis (the hard requirement)
|
|
13
|
+
|
|
14
|
+
The reaper must never reap a working session. Safety rests on positive evidence, not absence of activity:
|
|
15
|
+
- **Under-block (fails to reap a genuinely idle session):** acceptable — the existing 15m/4h idle-kill still runs; the reaper is additive pressure relief.
|
|
16
|
+
- **Over-block (reaps a working session):** the failure that matters. Mitigations: (1) requires a *positive* turn-complete idle-prompt signal; (2) render-stasis — pane byte-identical across all confirm ticks; (3) process + transcript must be quiet, and any *unresolvable* signal (no `claudeSessionId`, Codex/missing/rotated transcript, uninspectable process) forces KEEP, never "quiet"; (4) hysteresis; (5) two-phase reap with a final-grace re-check that aborts on any frame change; (6) Normal pressure tier reaps nothing; (7) bounded per-tick/per-hour budget; (8) auto-disable to dry-run on any ambiguous/failed reap; (9) ships OFF + dry-run.
|
|
17
|
+
|
|
18
|
+
## Level-of-abstraction / signal-vs-authority
|
|
19
|
+
|
|
20
|
+
Signals carry confidence and only *recommend*; kill authority sits behind the budget + dry-run + single-writer `terminateSession` CAS + auto-disable. The reaper computes a verdict; it does not own an unbounded kill.
|
|
21
|
+
|
|
22
|
+
## Interactions
|
|
23
|
+
|
|
24
|
+
- Composes with (does not fight) existing watchdogs: gate G defers to any recovery-in-flight (now incl. socket/silence); disjoint from OrphanProcessReaper (untracked procs) and SessionWatchdog (active-but-stuck); shares the single-writer kill path with the idle-kill so no double-kill / double-event.
|
|
25
|
+
- Pressure source is freemem-tiered for v1 (advisory; macOS under-reports). Crucially, an over-eager pressure tier can only reap a *genuinely-idle* session sooner — it cannot cause a working session to be reaped, because the classifier protects working sessions independent of tier.
|
|
26
|
+
|
|
27
|
+
## Rollback
|
|
28
|
+
|
|
29
|
+
Set `monitoring.sessionReaper.enabled:false` (the default) — fully inert. No data migration; `endedReason` is additive/optional. Revert the branch to remove code; no persisted state needs cleanup beyond an optional `state/session-reaper.json` (absent unless restart-durability is later wired).
|
|
30
|
+
|
|
31
|
+
## Tests
|
|
32
|
+
|
|
33
|
+
3-tier: unit (transcript prober, terminateSession CAS, classifier incl. every false-reap vector, config/migration), integration (`/sessions/reaper` + dry-run), e2e (feature-alive + dangerous cases). Wiring-integrity guards the construct→start→pass chain. Live test-as-self on a real in-flight build + a real Codex session precedes merge.
|
|
34
|
+
|
|
35
|
+
## Phase-3 review fixes (post multi-agent code review)
|
|
36
|
+
|
|
37
|
+
Independent review confirmed NO blocker to the hard requirement (cannot reap a working session) and surfaced safety-net hardening, all applied:
|
|
38
|
+
- **Reaping-lease leak:** when a matured reap is budget/tier-gated, the reaping lease is now released — previously it could permanently disable the fast idle-kill for that session.
|
|
39
|
+
- **Protected-list wiring:** gate A now reads `SessionManager.getProtectedSessions()` (the resolved list including the `<project>-server` default) rather than the raw config field, preventing spurious auto-disable when the server session goes idle.
|
|
40
|
+
- **Robustness:** `tick()` and `snapshot()` treat a throwing protect-signal as KEEP — never reap on a failed evaluation, and the `/sessions/reaper` route never 500s.
|
|
41
|
+
- **`killSession` contract preserved:** unconditional pane kill retained (only the in-flight guard added; no terminal-status early-return).
|
|
42
|
+
- **Known v1 gap (documented, not a false-reap vector):** the optional `mainProcessActive` CPU/IO-delta signal is not wired in v1; render-stasis is the real-time liveness channel that covers in-process work. Promoting `mainProcessActive` is a tracked enhancement, validated during the dry-run rollout.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Side-Effects Review — Threadline notification routing (CMT-519)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `threadline-notification-routing`
|
|
4
|
+
**Date:** 2026-05-25
|
|
5
|
+
**Author:** Echo
|
|
6
|
+
**Second-pass reviewer:** (pending — required; touches messaging routing + a structural redirect "guard")
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Threadline notifications no longer spawn a Telegram topic per event. `CollaborationSurfacer` gains a `notify()` entry (status/housekeeping → the single SILENT "Threadline" hub, never the parent topic), record-shaped surface state (with a legacy `string[]` read-migration) + bind helpers (`mostRecentUnbound`, `markBound`, `noteInHub`). The loop-gate (`src/commands/server.ts` ~7167) routes through `notify()` instead of `createAttentionItem`. `POST /attention` redirects threadline-class items to the hub. A new `POST /threadline/hub/bind` promotes ("open this") / binds ("tie this to X") a surfaced conversation to a topic — authoritatively setting `boundTopicId` + the commitment's `topicId`. Template + migration teach agents the hub + "open this" behavior. Decision points: the `/attention` reroute (routing, not block/allow), the loop-gate notice destination, the bind mutation.
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `POST /attention` threadline-class redirect — **add** — reroutes threadline/inter-agent/spawn items to the silent hub instead of a per-event topic.
|
|
15
|
+
- Loop-gate wind-down destination (`server.ts`) — **modify** — `createAttentionItem` → `collaborationSurfacer.notify()` (hub, not parent, not per-event topic).
|
|
16
|
+
- `POST /threadline/hub/bind` — **add** — authoritative thread→topic bind.
|
|
17
|
+
- CollaborationSurfacer status vs first-contact surfacing — **modify** — adds the status lane (`notify`) alongside the existing parentless first-contact lane (`surface`).
|
|
18
|
+
|
|
19
|
+
## 1. Over-block
|
|
20
|
+
No block/allow surface — these are routing/delivery decisions, not gates. The `/attention` redirect could mis-route a *legitimate general* item if its title coincidentally matches `threadline|inter-agent|spawn|relay` — mitigated by the category-first check (`/^(threadline|inter-agent|relay|spawn)/i` on category) plus content sniff only on distinctive phrases (`spawn-storm`, `spawn to receive`, `cannot spawn`, `inter-agent`, `\bthreadline\b`). A generic "spawn a new worker" general item would NOT match (no category prefix, no distinctive phrase). Worst case the item lands in the hub instead of its own topic — recoverable, not lost.
|
|
21
|
+
|
|
22
|
+
## 2. Under-block
|
|
23
|
+
A threadline alert posted via `/attention` with a *non-threadline category AND no distinctive phrase* would still get its own topic. Acceptable: the in-code threadline emitters (loop-gate) now route correctly; the redirect is a backstop for ad-hoc posts. The `TelegramBridge.mirrorInbound` per-thread topic path is intentionally excluded as a deliberate opt-in feature (default-off — the operator turned it on knowingly), not a notification the routing fix should override.
|
|
24
|
+
|
|
25
|
+
## 3. Level-of-abstraction fit
|
|
26
|
+
Correct: extended `CollaborationSurfacer` (already the hub owner) rather than a parallel router (convergence Q1). Status notices use a delivery sink (`notify`); real reply *content* still flows via the existing `TopicLinkageHandler` parent-topic path (one emitter per topic — avoids the double-notify the reviewer flagged, H2). The bind endpoint composes existing primitives (`ConversationStore.mutate`, `findOrCreateForumTopic`, `commitmentTracker.mutate`).
|
|
27
|
+
|
|
28
|
+
## 4. Signal vs authority compliance
|
|
29
|
+
No new blocking authority. The `/attention` redirect is a router (reroute + 201, never a hard block). `notify()`/`surface()` are delivery sinks. The bind endpoint mutates state on explicit operator action. Per `docs/signal-vs-authority.md`: detectors/sinks, not gates.
|
|
30
|
+
|
|
31
|
+
## 5. Interactions
|
|
32
|
+
- **No double-surface:** status (`notify`) → hub only; content (replies) → parent topic via TopicLinkageHandler only. The loop-gate path `return`s after notify, so it never also hits surface(). `surface()` (parentless first-contact) and `notify()` (status) are distinct lanes; a single inbound triggers at most one.
|
|
33
|
+
- **Bind vs first-write-wins:** `hub/bind` is authoritative — it overrides `captureOriginOnSend`'s anti-poisoning refusal by directly setting `boundTopicId` + the commitment `topicId` (operator intent > heuristic).
|
|
34
|
+
- **Legacy state migration** is read-time + idempotent; new record-shape round-trips.
|
|
35
|
+
|
|
36
|
+
## 6. External surfaces
|
|
37
|
+
New `transport`-free; new route `POST /threadline/hub/bind` (503 when telegram/conversationStore absent). Hub stays silent (`{silent:true}`) — no new buzzing. Template/migration change is agent-facing (Agent Awareness Standard) + idempotent (Migration Parity).
|
|
38
|
+
|
|
39
|
+
## 7. Rollback cost
|
|
40
|
+
Localized: CollaborationSurfacer (additive methods + schema with back-compat read), one server.ts callsite, two routes.ts additions, template + migration. Clean `git revert`. The surface-state schema change is forward+backward tolerant (load() reads both shapes); no data migration needed. New agents get the template via `generateClaudeMd`; existing via `migrateClaudeMd` (idempotent).
|
|
41
|
+
|
|
42
|
+
## Second-pass review
|
|
43
|
+
|
|
44
|
+
**Concur with the review** (independent reviewer, 2026-05-25). Verified all six checks against the diff: (1) the loop-gate `budgetExhausted && collaborationSurfacer` guard is sound (surfacer is `telegram ? new ... : undefined`), and the dropped `createAttentionItem` had no `/ack` consumer — nothing operational lost; (2) `load()` migration round-trips all three shapes (records / legacy string[] / dedicatedTopicId-only) with no loss of `dedicatedTopicId`; (3) the `/attention` redirect runs after `checkOutboundMessage`, matches only category-prefix `^(threadline|inter-agent|relay|spawn)` OR distinctive body tokens (8 realistic inputs tested — "CI failed", "relay race", "Spawning a new initiative" correctly do NOT match), else falls through; (4) `hub/bind` 503/404/409/200 paths correct, authoritative bind sets `boundTopicId` + commitment `topicId` via existing CAS-safe mutates, null-safe field access, all awaits correct, tsc clean; (5) migration content-sniff marker exactly matches the template heading — idempotent; (6) no double-surface — the `budgetExhausted` path `notify()`s then `return`s before `surface()`, and `surface()` early-returns on `hasParentTopic`; mutually exclusive per inbound.
|
|
45
|
+
|
|
46
|
+
Reviewer's one non-blocking gap — the new HTTP routes lacked Tier 2/3 coverage — has been **addressed**: added `tests/integration/threadline/hub-bind-routes.test.ts` (6 tests: 503/400/404/409/200-open/200-tie, asserting `boundTopicId` is set through the real `createRoutes` pipeline). The `/attention` redirect's match logic is covered by the reviewer's verified 8-input analysis + live test-as-self.
|