instar 1.3.5 → 1.3.7

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.
@@ -0,0 +1,36 @@
1
+ # Side-Effects Review — feedback-factory fingerprint port (Phase 1, increment 1)
2
+
3
+ **Slug:** `feedback-factory-fingerprint`
4
+ **Date:** `2026-05-26`
5
+ **Author:** Echo
6
+ **Spec:** `docs/specs/feedback-factory-migration.md` (converged v2, approved by Justin 2026-05-26)
7
+ **Scope:** Scar (b) only — the dedup fingerprinter. The first, foundational increment of the Phase-1 processor port. Other scars ((a) evidence gate, (c) false-merge/reopen/cycling, (d) lifecycle partitioning) and the processor-job wiring are subsequent increments.
8
+
9
+ ## Summary of the change
10
+
11
+ Ports `compute_fingerprint` + its `extract_component` dependency (and the `SEVERITY_PREFIXES`/`SEVERITY_PHRASES` sets) from the reference `the-portal/.claude/scripts/feedback-processor.py` to TypeScript at `src/feedback-factory/processor/fingerprint.ts`. This is the single most correctness-critical port in the migration — it decides whether two reports are the SAME bug. Adds:
12
+ - `src/feedback-factory/processor/fingerprint.ts` — the port (pure functions, no I/O).
13
+ - `tests/unit/feedback-factory/fingerprint.test.ts` — Tier-1 behavioral + both-sides-of-boundary tests + golden-value CI anchors.
14
+ - `scripts/feedback-factory/fingerprint-parity.mjs` + `_py_fingerprint_ref.py` + `fingerprint-corpus.json` — the LOCAL parity gate that runs the REAL reference Python and asserts byte-identical output.
15
+
16
+ The module is **not wired into anything yet** — it is a standalone, importable building block. No route, no job, no behavior change for any agent or user.
17
+
18
+ ## The byte-exact-port hazard and how it's handled
19
+
20
+ Per the spec, Python↔JS diverge on regex character classes, `\b`, `.lower()`, and encoding. The parity harness proved this empirically: a first cut diverged on a bare arabic-indic digit because Python's `\b\d+\b` is a Unicode word boundary while JS `\b` is ASCII-only. Fixed by emulating the Unicode boundary with lookarounds (`(?<![\p{L}\p{N}_])\p{Nd}+(?![\p{L}\p{N}_])`). After the fix: **33/33 corpus entries byte-identical to the reference Python**, including non-ASCII digits, em-dash, NBSP, Turkish İ/ı, German ß, hex hashes, and multi-version titles. Dawn's line-by-line review of the port remains a Phase-1 gate before any production cutover.
21
+
22
+ ## Seven-dimension review
23
+
24
+ 1. **Over/under-reach** — Pure functions, deterministic, no I/O, no global state. Not imported by any runtime path, so it cannot affect existing behavior. Risk is purely *equivalence to the reference*, addressed below.
25
+ 2. **Level-of-abstraction fit** — Lives at the processor-logic layer (`src/feedback-factory/processor/`), the correct home for the ported scar logic per the spec's open/operated architecture.
26
+ 3. **Signal vs Authority** — N/A; pure computation, no decision authority.
27
+ 4. **Interactions** — None. New isolated module; nothing imports it yet. The parity scripts shell out to an external reference path (configurable via `PORTAL_PROCESSOR`) and are LOCAL-only (not CI), so they add no CI dependency on the reference checkout.
28
+ 5. **Rollback cost** — Trivial: delete the module + tests + scripts. No data, no migration, no wiring to unwind.
29
+ 6. **Migration parity** — N/A. New internal library code shipped in the package; touches no agent-installed file (`.claude/settings.json` / config / CLAUDE.md template / hooks / skills).
30
+ 7. **Failure modes** — (a) Port diverges from the reference Python → caught by the parity harness (33/33 now) AND golden-value CI anchors in the unit test (so a future regex/encoding regression fails in CI even without the reference checkout). (b) Reference path absent → parity harness exits 2 with a clear message (it's a local gate, not CI). (c) An adversarial input not in the corpus diverges in production → mitigated by seeding the corpus with the known divergence classes; the corpus is extended as new classes are discovered, and Dawn's review is the human backstop.
31
+
32
+ ## Tests
33
+
34
+ - Tier-1 unit (CI): `tests/unit/feedback-factory/fingerprint.test.ts` — `extractComponent` (dotted id, first-word fallback, [TAG] strip, whitelisted-vs-not severity prefix, empty); `computeFingerprint` (32-hex shape, version/int/hash collapse, internal-whitespace collapse, leading-whitespace component divergence, case-insensitivity, type+component incorporation, 3 golden anchors). 14 tests.
35
+ - Parity (local gate, evidence): `scripts/feedback-factory/fingerprint-parity.mjs` → **33/33 byte-identical** to the reference Python.
36
+ - No integration/E2E tier this increment: the module is not yet wired to a route or job; those tiers attach when the processor job + receiver land (subsequent increments). Documented here so the omission is a reasoned decision, not an oversight.
@@ -0,0 +1,68 @@
1
+ # Side-Effects Review — Stage-B auto-capture + funnel (Mentor System §19.2)
2
+
3
+ **Spec:** `docs/specs/FRAMEWORK-ONBOARDING-MENTOR-SPEC.md` (converged 5 iters, approved by Justin)
4
+ **Change:** Adds the Stage-B auto-capture entry point `FrameworkIssueLedger.captureRun()` + a
5
+ `framework_capture_runs` funnel table + `captureStats()` + a read-only route
6
+ `GET /framework-issues/capture-stats`. Builds on §19.1 (the ledger foundation, PR #405).
7
+ **Files:** `src/monitoring/FrameworkIssueLedger.ts`, `src/server/routes.ts`,
8
+ `src/server/CapabilityIndex.ts`, `tests/unit/FrameworkIssueLedger.test.ts`,
9
+ `tests/integration/framework-issues-routes.test.ts`,
10
+ `tests/e2e/framework-issue-ledger-lifecycle.test.ts`, `upgrades/NEXT.md`.
11
+
12
+ ## Principle check (Phase 1)
13
+
14
+ Does this involve a decision point that gates info flow / blocks actions / constrains behavior?
15
+ **No.** `captureRun()` is the write-path the mentor tick will call after forensics — it records
16
+ findings and logs the run. It holds no blocking authority. The capture-stats route is read-only
17
+ observability. Signal-only. The decision-bearing components (Stage A, the job, graduation) are
18
+ §19.3–5.
19
+
20
+ ## The seven questions
21
+
22
+ 1. **Over-block.** None. `captureRun` writes whatever findings it's given (validated against the
23
+ same enum allowlists as `recordObservation`); it rejects only structurally-invalid findings
24
+ (bad bucket/severity), which is correct.
25
+
26
+ 2. **Under-block.** The funnel is the explicit guard against the under-detection failure mode it
27
+ exists for: a run that writes nothing is still logged, so an inert/broken writer (runs climbing,
28
+ observations flat) is visible. It does not *interpret* that signal (no alerting yet) — surfacing
29
+ is via the read-only route; an alert/threshold is a later concern (§19.5 observability surface),
30
+ not under-blocking here. There is still no public write route, so no untrusted-write surface.
31
+
32
+ 3. **Level-of-abstraction fit.** `captureRun` lives ON the ledger (not a separate DI component)
33
+ because better-sqlite3 has no nested transactions and the capture is a thin orchestration over
34
+ `recordObservation` + `suggestRegressions` + a run-log insert. One atomic entry point for the
35
+ tick to call is the right seam; a separate service would add wiring with no benefit.
36
+
37
+ 4. **Signal vs authority.** Compliant. `captureRun` writes signal; regression candidates are
38
+ *surfaced, never auto-linked* (the writer doesn't get to decide a regression — §13.5). Promotion
39
+ and graduation authority stay with the human (§6/§8).
40
+
41
+ 5. **Interactions.** `captureRun` calls `recordObservation` per finding (each its own txn — no
42
+ nested-transaction violation), then writes one `framework_capture_runs` row. Episode-collapsing
43
+ in `recordObservation` already prevents double-counting across runs, so re-observing the same
44
+ open issue next tick does not inflate `observationsWritten`. The funnel table is independent of
45
+ the issues/observations tables — no shadowing.
46
+
47
+ 6. **External surfaces.** One new read-only route (`/framework-issues/capture-stats`) behind the
48
+ standard Bearer middleware, added to the existing `frameworkIssues` CapabilityIndex entry (no
49
+ new prefix → no discoverability-classification gap). No agent-facing template change needed
50
+ beyond §19.1's. No timing/conversation-state dependence.
51
+
52
+ 7. **Rollback cost.** Low. Still dormant — no production caller until the mentor job (§19.4). The
53
+ `framework_capture_runs` table auto-creates at startup (idempotent `CREATE TABLE IF NOT EXISTS`)
54
+ and is harmless read-only data. Back-out = revert; the table can stay or be dropped.
55
+
56
+ ## Phase 5 — second-pass
57
+
58
+ **Not required.** Read-only observability + a signal-only write-path; no block/allow, no
59
+ session-lifecycle, nothing named sentinel/guard/gate/watchdog. The spec passed full convergence.
60
+
61
+ ## Testing
62
+
63
+ - Tier 1 (unit, +6 = 28 total): captureRun writes findings + reports summary; **every run logged
64
+ to the funnel including a zero-finding run** (inert-writer guard); no double-count across runs;
65
+ regression candidates surfaced not auto-linked; per-framework funnel breakdown; enum guard.
66
+ - Tier 2 (integration, +2 = 11 total): `/framework-issues/capture-stats` 503 + 200-with-funnel.
67
+ - Tier 3 (e2e, +1 = 7 total): a real capture run surfaces in the funnel route on the live server.
68
+ - Affected push-config suite (vs JKHeadley/main): 842 + 299 capability tests green, no regressions.