instar 1.3.12 → 1.3.14
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/feedback-factory/processor/cluster.d.ts +33 -0
- package/dist/feedback-factory/processor/cluster.d.ts.map +1 -0
- package/dist/feedback-factory/processor/cluster.js +120 -0
- package/dist/feedback-factory/processor/cluster.js.map +1 -0
- package/dist/feedback-factory/processor/types.d.ts +46 -0
- package/dist/feedback-factory/processor/types.d.ts.map +1 -0
- package/dist/feedback-factory/processor/types.js +10 -0
- package/dist/feedback-factory/processor/types.js.map +1 -0
- package/dist/scheduler/MentorOnboardingRunner.d.ts +19 -0
- package/dist/scheduler/MentorOnboardingRunner.d.ts.map +1 -1
- package/dist/scheduler/MentorOnboardingRunner.js +44 -1
- package/dist/scheduler/MentorOnboardingRunner.js.map +1 -1
- package/dist/scheduler/MentorOnboardingTick.d.ts +10 -0
- package/dist/scheduler/MentorOnboardingTick.d.ts.map +1 -1
- package/dist/scheduler/MentorOnboardingTick.js +8 -0
- package/dist/scheduler/MentorOnboardingTick.js.map +1 -1
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +32 -3
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +10 -10
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/scripts/feedback-factory/_py_clustering_ref.py +56 -0
- package/scripts/feedback-factory/clustering-corpus.json +17 -0
- package/scripts/feedback-factory/clustering-parity.mjs +71 -0
- package/src/data/builtin-manifest.json +47 -47
- package/upgrades/1.3.13.md +37 -0
- package/upgrades/1.3.14.md +26 -0
- package/upgrades/side-effects/feedback-factory-clustering.md +34 -0
- package/upgrades/side-effects/mentor-live-readiness.md +63 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Side-Effects Review — feedback-factory clustering driver (Phase 1, increment 4)
|
|
2
|
+
|
|
3
|
+
**Slug:** `feedback-factory-clustering`
|
|
4
|
+
**Date:** `2026-05-27`
|
|
5
|
+
**Author:** Echo (autonomous)
|
|
6
|
+
**Spec:** `docs/specs/feedback-factory-migration.md` (converged v2, approved by Justin 2026-05-26)
|
|
7
|
+
**Scope:** The clustering decision loop of `cmd_cluster` (:1405) — assign each unprocessed item to an existing cluster or create a new one. Pure orchestration over the already-parity'd Jaccard similarity. The DB read/write halves (the queries + `cmd_apply_clusters`) are the store/adapter layer, a later increment.
|
|
8
|
+
|
|
9
|
+
## Summary of the change
|
|
10
|
+
|
|
11
|
+
Byte-exact port of `cmd_cluster`'s decision loop from `the-portal/.claude/scripts/feedback-processor.py` to `src/feedback-factory/processor/cluster.ts` as the pure function `clusterItems(items, clusters)`. Adds shared types (`src/feedback-factory/processor/types.ts`: `FeedbackItem`/`Cluster`/`ClusterResult`), the clustering parity harness, and Tier-1 unit tests. Pure functions, no I/O. **Not wired into any route/job yet** — no behavioral change.
|
|
12
|
+
|
|
13
|
+
The decision rules ported: per item, pick the highest-Jaccard cluster over `"{title} {description}"`; merge if score ≥ threshold (0.55 for `fixed`/`resolved`/`fix_applied` clusters — the false-merge guard — else 0.35); else create a new `cluster-<slug>` and make it a candidate for later items in the batch (order-dependent, by design). Regression/reopen merge-notes preserved.
|
|
14
|
+
|
|
15
|
+
## Equivalence verification
|
|
16
|
+
|
|
17
|
+
- **7/7 fixture decisions match the REAL `cmd_cluster`.** The parity harness monkeypatches the reference's `run_prisma_query` to feed (items, clusters) fixtures and runs cmd_cluster's actual, unmodified loop, capturing its returned results — so this is parity against the real code, not a re-implementation. Matched fields: action, clusterId, rounded similarity, and merge note. Fixture exercises create / merge-open / the 0.55 false-merge guard / merge-into-fixed (regression) / merge-into-deferred (reopen) / order-dependence / slug.
|
|
18
|
+
- Builds on the already-parity'd `jaccardSimilarity` (increment 2, 20/20), so the only new surface is the orchestration + `pyRound3` (Python round-half-to-even, unit-tested on exact-half cases).
|
|
19
|
+
|
|
20
|
+
## Seven-dimension review
|
|
21
|
+
|
|
22
|
+
1. **Over/under-reach** — Pure deterministic function, no I/O, no global state, not imported by any runtime path. Works on a shallow COPY of the clusters array so the caller's input isn't mutated (while still letting created clusters match later items, as the reference does).
|
|
23
|
+
2. **Level-of-abstraction fit** — Processor-logic layer, on top of the similarity primitive. The DB I/O (reads + `cmd_apply_clusters` writes) is deliberately NOT here — it belongs to the store/adapter layer, keeping this pure + parity-testable.
|
|
24
|
+
3. **Signal vs Authority** — N/A; pure computation producing a decision list. The FALSE-MERGE-GUARD near-miss LOG line is intentionally NOT emitted by the pure function (it's a stdout side effect of the reference); the observability layer emits it. The DECISION (create vs merge) is identical.
|
|
25
|
+
4. **Interactions** — None. New isolated module; nothing imports it yet. Parity scripts LOCAL-only.
|
|
26
|
+
5. **Rollback cost** — Trivial: delete the module + tests + scripts. `types.ts` is additive.
|
|
27
|
+
6. **Migration parity** — N/A. New internal library code; no agent-installed file touched.
|
|
28
|
+
7. **Failure modes** — (a) Port diverges from reference → parity harness (7/7, against the real loop) + unit tests. (b) `pyRound3` half-rounding divergence → unit-tested on exact-half cases + parity. (c) Order-dependence not reproduced → unit + parity both exercise a later-item-matches-earlier-created-cluster case. (d) The FALSE-MERGE-GUARD stdout log isn't reproduced → intentional (pure function); documented; the observability increment will emit it.
|
|
29
|
+
|
|
30
|
+
## Tests
|
|
31
|
+
|
|
32
|
+
- Tier-1 unit (CI): `tests/unit/feedback-factory/cluster.test.ts` — pyRound3 (incl. half-to-even), create/merge, the 0.55 guard, regression + reopen notes, order-dependence. 8 tests.
|
|
33
|
+
- Parity (local gate, evidence): `scripts/feedback-factory/clustering-parity.mjs` → **7/7** vs the real `cmd_cluster`.
|
|
34
|
+
- No integration/E2E this increment: not wired to a route/job; those tiers attach when the store/apply layer + job land. Reasoned decision, documented.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Side-Effects Review — Mentor live-readiness (§19.4 follow-on)
|
|
2
|
+
|
|
3
|
+
**Spec:** `docs/specs/FRAMEWORK-ONBOARDING-MENTOR-SPEC.md` (converged 5 iters, approved by Justin)
|
|
4
|
+
**Change:** Closes the three live-promotion blockers the §19.4 Phase-5 reviewer flagged, so the
|
|
5
|
+
mentor can be promoted toward `live` safely: (1) **async tick** — `POST /mentor/tick` is now
|
|
6
|
+
fire-and-forget (202) with the result in `GET /mentor/status`.lastResult, so a slow Stage-A spawn
|
|
7
|
+
can't hang the request; (2) **spawn-kill-on-timeout** — `spawnStageA` kills the session and throws
|
|
8
|
+
on poll exhaustion (no orphaned tmux pane, no partial transcript); (3) **persist-only delivery** —
|
|
9
|
+
`live` mode delivers the Stage-A message by appending to a durable per-mentee outbox, never via
|
|
10
|
+
`threadline_send`/spawn (the structural fix for the cross-agent spawn loop).
|
|
11
|
+
**Files:** `src/scheduler/MentorOnboardingTick.ts`, `src/scheduler/MentorOnboardingRunner.ts`,
|
|
12
|
+
`src/server/routes.ts`, `src/server/AgentServer.ts`, the three mentor test files, `upgrades/NEXT.md`.
|
|
13
|
+
|
|
14
|
+
## Principle check (Phase 1)
|
|
15
|
+
|
|
16
|
+
Decision point? Same as §19.4 — signal-only loop. The new delivery path is the only outbound
|
|
17
|
+
action, and it is **persist-only** (queue append), gated to `live` mode, and structurally cannot
|
|
18
|
+
spawn a counterpart session. Still ships dormant (`mentor.enabled=false`).
|
|
19
|
+
|
|
20
|
+
## The seven questions
|
|
21
|
+
|
|
22
|
+
1. **Over-block.** Async tick returns 202 even if a tick is in-flight (`accepted:false, reason:
|
|
23
|
+
in-flight`) — correct (no overlapping ticks). No legitimate action blocked.
|
|
24
|
+
2. **Under-block.** Spawn-kill-on-timeout closes the orphan/partial-transcript gap. Delivery is
|
|
25
|
+
persist-only — it queues; whether the mentee's session ingests the outbox is the mentee's side,
|
|
26
|
+
validated at the live step. The outbox write is best-effort (never throws) so a delivery failure
|
|
27
|
+
can't crash a tick — a dropped message is preferable to a crashed loop while dormant/dry-run.
|
|
28
|
+
3. **Level-of-abstraction fit.** Async state lives on the runner (`inFlight` + `lastResult`); the
|
|
29
|
+
route is a thin 202 trigger. Delivery is an injected service (pure tick stays I/O-free; the
|
|
30
|
+
AgentServer impl is the only thing that touches disk). Correct layering.
|
|
31
|
+
4. **Signal vs authority.** Compliant. Delivery is the loop's only outbound effect, persist-only,
|
|
32
|
+
live-gated. No component is gated/killed/blocked. The tick still has no authority over the user.
|
|
33
|
+
5. **Interactions.** `killSession` on timeout reuses the existing SessionManager kill path. The
|
|
34
|
+
outbox is a new dedicated dir (`server-data/mentor-outbox/`), isolated from the ledger and other
|
|
35
|
+
stores. The in-flight guard prevents the heartbeat job from stacking ticks.
|
|
36
|
+
6. **External surfaces.** `POST /mentor/tick` now returns 202 (was 200 with the result) for the
|
|
37
|
+
enabled case; disabled still returns 200 `{ran:false,reason:disabled}`. `GET /mentor/status` gains
|
|
38
|
+
`inFlight` + `lastResult`. The outbox is a local file, not an external call. Still Bearer-gated.
|
|
39
|
+
7. **Rollback cost.** Low. Dormant; revert restores the synchronous tick. The outbox dir is harmless
|
|
40
|
+
queued data.
|
|
41
|
+
|
|
42
|
+
## Phase 5 — second-pass
|
|
43
|
+
|
|
44
|
+
The §19.4 live loop already carried a dedicated second-pass (concur, 0 blocking). This PR
|
|
45
|
+
*implements that reviewer's three flagged fixes verbatim* (async tick, kill-on-timeout, persist-only
|
|
46
|
+
delivery), so it closes their concerns rather than introducing new decision surface. Self-reviewed
|
|
47
|
+
against their notes; no new spawning/gating logic beyond the persist-only outbox (which is the
|
|
48
|
+
spawn-loop-safe shape they recommended).
|
|
49
|
+
|
|
50
|
+
## Remaining before `live`
|
|
51
|
+
|
|
52
|
+
`dailySpendCapUsd` is still a run-count cap (`maxRoundsPerDay`, a real hard spend bound) rather than
|
|
53
|
+
dollar accounting — tracked (<!-- tracked: topic-13435 -->). Deep Stage-B forensics + surface
|
|
54
|
+
assembly land in the next PR. End-to-end mentee ingestion of the outbox is validated at the live
|
|
55
|
+
step (test-as-self), which remains Justin's sign-off per the off→dry-run→live track.
|
|
56
|
+
|
|
57
|
+
## Testing
|
|
58
|
+
|
|
59
|
+
- Tier 1 (unit, +3 tick): delivery in live-only / not dry-run / safe-when-unwired; (+2 runner):
|
|
60
|
+
startTick fire-and-forget 202 + result lands in status; disabled short-circuit.
|
|
61
|
+
- Tier 2 (integration): `/mentor/tick` 202-when-enabled + status.lastResult; disabled 200.
|
|
62
|
+
- Tier 3 (e2e): unchanged — dormant on the production path.
|
|
63
|
+
- route-completeness + discoverability gates pass; affected push-config suite green (745) vs main.
|