instar 1.3.482 → 1.3.484
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/core/crossModelReviewer.d.ts +139 -9
- package/dist/core/crossModelReviewer.d.ts.map +1 -1
- package/dist/core/crossModelReviewer.js +342 -16
- package/dist/core/crossModelReviewer.js.map +1 -1
- package/package.json +1 -1
- package/skills/spec-converge/SKILL.md +57 -17
- package/skills/spec-converge/scripts/cross-model-review.mjs +113 -15
- package/skills/spec-converge/scripts/write-convergence-tag.mjs +111 -5
- package/skills/spec-converge/templates/reviewer-decision-completeness.md +83 -0
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/1.3.483.md +28 -0
- package/upgrades/1.3.484.md +37 -0
- package/upgrades/side-effects/cross-model-hardening.md +93 -0
- package/upgrades/side-effects/decision-completeness-gate.md +85 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Reviewer Prompt — Decision Completeness Perspective
|
|
2
|
+
|
|
3
|
+
You are the decision-completeness reviewer for an instar spec under convergence review.
|
|
4
|
+
|
|
5
|
+
Read these in order:
|
|
6
|
+
|
|
7
|
+
1. The spec file at {SPEC_PATH}
|
|
8
|
+
2. Any architectural doc the spec references.
|
|
9
|
+
|
|
10
|
+
Your DECISION-COMPLETENESS perspective (Autonomy Principle 2 — "frontload all user
|
|
11
|
+
decisions into the spec so the agent completes it in a SINGLE autonomous run"): your
|
|
12
|
+
ONLY job is to find every point where the building agent would have to **stop
|
|
13
|
+
mid-run and ask the user**, and force each one to be settled NOW, in the spec —
|
|
14
|
+
because agents build at 100–1000x behind dark/dry-run/read-only phases, a decision is
|
|
15
|
+
cheaper to change *after* a completed run than to stop-and-wait mid-run for.
|
|
16
|
+
|
|
17
|
+
Enumerate every mid-run stop-and-ask point. Each MUST be one of:
|
|
18
|
+
|
|
19
|
+
1. **Frontloaded** — pulled into the spec's `## Frontloaded Decisions` section as a
|
|
20
|
+
concrete decision (made by the author, attributable, reversible-or-not stated). A
|
|
21
|
+
decision the user must make belongs in `## Open questions` — and the spec CANNOT
|
|
22
|
+
converge while any remain (the convergence criterion enforces this; your job is to
|
|
23
|
+
FIND the buried ones that never made it into either section).
|
|
24
|
+
|
|
25
|
+
2. **Cheap-to-change-after** — explicitly tagged as a default safe to pick now and
|
|
26
|
+
change post-run, *because* the work ships behind a named dark / dry-run /
|
|
27
|
+
read-only phase.
|
|
28
|
+
|
|
29
|
+
**CONTEST every cheap-to-change-after tag.** Do not merely check that a phase is
|
|
30
|
+
named — independently assert reversibility. A closed NON-CHEAP taxonomy overrides any
|
|
31
|
+
tag: anything touching
|
|
32
|
+
|
|
33
|
+
- **durable external side-effects** (sent messages, published pages, external-system
|
|
34
|
+
writes, deleted data),
|
|
35
|
+
- **money** (spend, billing, subscriptions),
|
|
36
|
+
- **identity** (keys, principals, operator binding, trust levels), or
|
|
37
|
+
- **a published / user-visible interface** (API contracts others consume, dashboards
|
|
38
|
+
users rely on, on-disk formats other tools read)
|
|
39
|
+
|
|
40
|
+
is NEVER cheap-to-change-after, regardless of a "ships dark" label — the dark phase
|
|
41
|
+
ends, and what happened during it does not un-happen. A contested tag you reject is a
|
|
42
|
+
**MATERIAL finding that blocks convergence** — same authority as any other material
|
|
43
|
+
issue.
|
|
44
|
+
|
|
45
|
+
Specifically check:
|
|
46
|
+
|
|
47
|
+
1. **Buried decisions** — sentences like "the implementer can decide", "TBD", "we
|
|
48
|
+
could either X or Y", "depending on user preference", "ask the user when" anywhere
|
|
49
|
+
in the body. Each is an un-frontloaded decision hiding outside `## Open questions`.
|
|
50
|
+
|
|
51
|
+
2. **Unstated defaults** — config values, thresholds, naming, storage choices the
|
|
52
|
+
spec leaves open. Each needs a concrete default (frontloaded) or a contested-and-
|
|
53
|
+
surviving cheap tag.
|
|
54
|
+
|
|
55
|
+
3. **Conditional scope** — "if X turns out to be true, then…" branches whose
|
|
56
|
+
resolution requires a human answer mid-build. Force the branch to be decided now
|
|
57
|
+
or restructured so either outcome is buildable without asking.
|
|
58
|
+
|
|
59
|
+
4. **Approval points** — any step where the spec says to pause for sign-off mid-run
|
|
60
|
+
(deploy gates, "confirm with the user before…"). Distinguish a genuine
|
|
61
|
+
operator-only authority (legitimate — but then the spec must scope the single run
|
|
62
|
+
to END before it, with the gate as the run boundary) from ceremony (a material
|
|
63
|
+
finding: remove it or convert it to a post-run review).
|
|
64
|
+
|
|
65
|
+
5. **Cheap-tag audit** — for every existing cheap-to-change-after tag: does the named
|
|
66
|
+
dark/dry-run/read-only phase actually exist in the spec's rollout plan? Does the
|
|
67
|
+
tagged decision hit the non-cheap taxonomy above? Reject violators.
|
|
68
|
+
|
|
69
|
+
6. **Open-questions hygiene** — does `## Open questions` exist, and is every entry a
|
|
70
|
+
genuine user decision (not a design question the author should answer)? An
|
|
71
|
+
author-answerable question parked on the user is a deferral, not a decision.
|
|
72
|
+
|
|
73
|
+
Produce a SHORT report (under 400 words):
|
|
74
|
+
|
|
75
|
+
- **Verdict: CLEAN, MINOR ISSUES, or SERIOUS ISSUES**
|
|
76
|
+
- Counts: `frontloaded-decisions: N`, `cheap-tags: N`, `contested-then-cleared: N`,
|
|
77
|
+
`contested-rejected: N`, `open-user-decisions: N`
|
|
78
|
+
- Specific findings with spec-section references and concrete resolutions (which
|
|
79
|
+
section the decision must move to, what default you recommend, which cheap tag you
|
|
80
|
+
reject and why).
|
|
81
|
+
|
|
82
|
+
Be rigorous. A spec converges only when an agent could complete it in ONE autonomous
|
|
83
|
+
run without a single mid-run "what do you want me to do?"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-06-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-06-11T00:03:52.959Z",
|
|
5
|
+
"instarVersion": "1.3.484",
|
|
6
6
|
"entryCount": 201,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Implements Piece 2 of `docs/specs/AUTONOMY-PRINCIPLES-ENFORCEMENT-SPEC.md` (Autonomy Principle 2 — frontload all user decisions so a spec completes in ONE autonomous run). Three parts, all in the agent-private spec-converge skill (no runtime `src/` surface):
|
|
9
|
+
|
|
10
|
+
1. **New internal reviewer** (`reviewer-decision-completeness.md`, 6th of six): enumerates every mid-run stop-and-ask-the-user point; each must be frontloaded into `## Frontloaded Decisions` or tagged cheap-to-change-after behind a named dark/dry-run/read-only phase. The reviewer CONTESTS every cheap tag against a closed non-cheap taxonomy (durable external side-effects, money, identity, published/user-visible interface — never cheap); a rejected tag is a material finding that blocks convergence. Applies to ALL specs (D7), no per-spec override (D11).
|
|
11
|
+
2. **New convergence criterion** (Phase 3): a spec cannot converge while `## Open questions` contains an unresolved user-decision.
|
|
12
|
+
3. **Structural enforcement + earned evidence** (`write-convergence-tag.mjs`): the tag writer refuses to stamp `review-convergence` while open questions remain, and writes `single-run-completable: true` plus the reviewer's counts (`frontloaded-decisions`, `cheap-to-change-tags`, `contested-then-cleared`) — the tag carries its evidence, earned not minted. Pre-existing converged specs are unaffected (the gate fires at stamp time only).
|
|
13
|
+
|
|
14
|
+
The spec's migration decision is resolved and recorded: spec-converge stays **agent-private** (matching `/instar-dev`'s not-user-facing status) — deliberately NOT promoted to the fleet builtin skill set, so no fleet migration surface exists.
|
|
15
|
+
|
|
16
|
+
## What to Tell Your User
|
|
17
|
+
|
|
18
|
+
None — internal change (no user-facing surface).
|
|
19
|
+
|
|
20
|
+
## Summary of New Capabilities
|
|
21
|
+
|
|
22
|
+
None — internal change (no user-facing surface).
|
|
23
|
+
|
|
24
|
+
## Evidence
|
|
25
|
+
|
|
26
|
+
- `tests/unit/write-convergence-tag-decision-completeness.test.ts`: 15 new tests — none-marker variants, section scoping, refuse-on-live-question (tag NOT written), stamp-on-resolved, earned counts, no-counts → no minted tag, idempotent re-runs.
|
|
27
|
+
- `tests/unit/write-convergence-tag-crossmodel.test.ts`: 16 existing tests green (no regression from the import-safe main-guard restructure).
|
|
28
|
+
- Live functional run: refused a spec with `- **Q1:** should we do A or B?` (exit 1 + remediation message); stamped `single-run-completable: true` + counts once the section read `*(none)*`.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: minor -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Implements Piece 3 (final piece) of `docs/specs/AUTONOMY-PRINCIPLES-ENFORCEMENT-SPEC.md`. The spec-converge skill's external (non-Claude) review pass becomes mandatory with teeth, dynamic, and safe:
|
|
9
|
+
|
|
10
|
+
- **gemini-cli joins the reviewer registry** (`src/core/crossModelReviewer.ts`) — the registry seam's first extension beyond codex: never-throws detection (binary + cached OAuth at the path the CLI actually reads), provider via the existing factory with its own circuit breaker, identical degraded semantics.
|
|
11
|
+
- **Family-diverse**: one external pass per AVAILABLE family (detect-all), not first-match-only.
|
|
12
|
+
- **Delta-gated**: a content hash of the spec's reviewable body (frontmatter-stripped) decides when externals must re-run — round 1 and any changed round; unchanged rounds record a skip-with-logged-note instead of burning external quota.
|
|
13
|
+
- **Durable activation baseline**: framework availability observations are recorded to `state/framework-activation-history.jsonl`; externals are non-skippable whenever a non-Claude framework was active within a 7-day lookback — deactivating a framework just before converging no longer exempts a spec.
|
|
14
|
+
- **Trusted-provider allowlist**: only first-party OAuth CLIs (codex-cli, gemini-cli) can receive spec text; pi-cli/custom endpoints are structurally excluded (`untrusted-framework` refusal).
|
|
15
|
+
- **Fail-loud model canary**: a review pass degrades loudly (`model-resolution-canary`) rather than silently running with an unresolved tier-word model.
|
|
16
|
+
|
|
17
|
+
Honest scope notes: the spec's "broken `resolveModelForFramework` foundation" was already fixed on main before this build (recorded at PR #1055); the Claude-only-agent different-family floor + tracked-gap signal are disclosed residuals (see the side-effects artifact).
|
|
18
|
+
|
|
19
|
+
## What to Tell Your User
|
|
20
|
+
|
|
21
|
+
- "When I review a spec before building, I now get a second opinion from EVERY non-Claude AI family I have access to (GPT via codex, Gemini via the gemini CLI) — automatically, on every round where the spec actually changed. I can't quietly skip it anymore: a record of which frameworks I've had active in the last week keeps the outside-opinion requirement honest."
|
|
22
|
+
- "Your spec text only ever goes to first-party AI providers you've logged into — never to custom or unknown endpoints."
|
|
23
|
+
|
|
24
|
+
## Summary of New Capabilities
|
|
25
|
+
|
|
26
|
+
| Capability | How to Use |
|
|
27
|
+
|-----------|-----------|
|
|
28
|
+
| Gemini external reviewer | Automatic in spec-converge when the gemini CLI is installed + authed |
|
|
29
|
+
| Family-diverse external pass | Automatic — one pass per available family per changed round |
|
|
30
|
+
| Delta-gated externals | Automatic — unchanged rounds log a skip note instead of re-reviewing |
|
|
31
|
+
| 7-day activation baseline | Automatic — recorded on every detection; read at convergence |
|
|
32
|
+
|
|
33
|
+
## Evidence
|
|
34
|
+
|
|
35
|
+
- 32 new unit tests (`crossModelReviewer-piece3.test.ts`) + 45 existing (`crossModelReviewer.test.ts`) + 3 integration (`cross-model-review-flow.test.ts`) — all green; `tsc` exit 0; full lint clean.
|
|
36
|
+
- Live smoke on the dev machine: detect-all reported codex-cli:gpt-5.5 AND gemini-cli:gemini-2.5-pro; `--family pi-cli` refused with `untrusted-framework`; the activation observation landed in the JSONL.
|
|
37
|
+
- Independent second-pass review verified the egress allowlist end-to-end and caught a real bug (a `GEMINI_HOME` env var the CLI doesn't actually honor) — fixed before ship.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Side-Effects Review — Cross-Model Convergence Hardening (Autonomy Principles Enforcement, Piece 3)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `cross-model-hardening`
|
|
4
|
+
**Date:** `2026-06-10`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `independent reviewer subagent (see appended verdict)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Implements Piece 3 of `docs/specs/AUTONOMY-PRINCIPLES-ENFORCEMENT-SPEC.md`: cross-model convergence becomes **mandatory (with a durable activation baseline), dynamic (no pinning), family-diverse, delta-gated, allowlist-constrained, and fail-loud**. Honest scope note: the spec's headline "broken foundation" claim (`resolveModelForFramework` returning a dead tier string for gemini/pi) was **already fixed on main** before this build — recorded at PR #1055; this PR implements everything else.
|
|
11
|
+
|
|
12
|
+
- **gemini-cli reviewer registry entry** (`src/core/crossModelReviewer.ts`) — the registry seam's first extension beyond codex: injectable never-throws detection (binary + cached OAuth creds), provider via the existing factory (own circuit breaker), identical degraded semantics.
|
|
13
|
+
- **Family-diverse collection** — `detectAllCrossModelReviewers()` returns ALL available frameworks; the skill now runs one external pass per available family instead of first-match-only.
|
|
14
|
+
- **Delta-gating** — `hashSpecReviewableBody()` (sha256, frontmatter-stripped, CRLF-normalized); externals are mandatory on round 1 + any round where the reviewable body changed; an unchanged round records a skip-with-logged-note (≠ skipped-abbreviated).
|
|
15
|
+
- **Durable activation baseline** — `recordFrameworkActivationObservation()` / `wasNonClaudeFrameworkActiveWithin()` (JSONL at `state/framework-activation-history.jsonl`, 2000-line cap): externals are non-skippable whenever a non-Claude framework was active at any point in a 7-day lookback — a just-before-converge deactivation no longer exempts a spec (round-2 adversarial F-R2-3).
|
|
16
|
+
- **Trusted-provider allowlist** — `TRUSTED_REVIEWER_FRAMEWORKS = ['codex-cli','gemini-cli']`; the registry constructively carries only first-party OAuth CLIs; `--family pi-cli` (or any unlisted id) is refused (`untrusted-framework`) so the full spec text is never sent to a custom/base-URL endpoint.
|
|
17
|
+
- **Fail-loud canary** — `isConcreteReviewerModel()`: both entries degrade LOUDLY (`model-resolution-canary`) rather than silently reviewing with a bare tier word.
|
|
18
|
+
|
|
19
|
+
Files: `src/core/crossModelReviewer.ts`, `skills/spec-converge/scripts/cross-model-review.mjs`, `skills/spec-converge/SKILL.md`, `tests/unit/crossModelReviewer-piece3.test.ts` (new, 32 tests), hermeticity fixes in 2 existing test files.
|
|
20
|
+
|
|
21
|
+
## Decision-point inventory
|
|
22
|
+
|
|
23
|
+
- Cross-model framework detection/selection — **modify** — first-match → all-available (collection only; no blocking authority).
|
|
24
|
+
- `--family` allowlist guard — **add** — hard-invariant refusal of an untrusted framework id at the script edge (egress protection, enumerable set).
|
|
25
|
+
- Externals-mandatory check (SKILL.md + `wasNonClaudeFrameworkActiveWithin`) — **add** — a deterministic read over the durable history that decides whether the SKILL FLOW may skip an external pass. Process-gating inside spec review; blocks no agent action or message.
|
|
26
|
+
- Model canary — **add** — degrades a review pass; blocks nothing.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 1. Over-block
|
|
31
|
+
|
|
32
|
+
The allowlist refusing `pi-cli` could be seen as over-blocking a legitimate reviewer — deliberate: pi is multi-provider by design (its model flag may resolve to a custom endpoint), and the cost of a wrongly-allowed egress (full spec text to an attacker-controlled model) dwarfs the cost of one fewer reviewer family. The activation lookback could keep externals mandatory for an agent that genuinely uninstalled a framework yesterday — accepted: the 7-day tail is the entire point (anti-gaming), and the pass degrades to advisory-unavailable rather than hard-failing when the framework is truly gone.
|
|
33
|
+
|
|
34
|
+
## 2. Under-block
|
|
35
|
+
|
|
36
|
+
The activation history only records when `--detect-only`/review runs execute — an agent that NEVER ran spec-converge in the lookback has an empty history and presents as single-framework. Acceptable: the first detect of the current convergence records the observation, so the gaming window is "deactivate before the FIRST round" — and a genuinely deactivated-everywhere agent degrades to the advisory floor exactly as specced. The history is also local-file mutable by the agent itself (not tamper-proof) — consistent with every other instar state file; the report banner + frontmatter flag remain the human-visible disclosure.
|
|
37
|
+
|
|
38
|
+
## 3. Level-of-abstraction fit
|
|
39
|
+
|
|
40
|
+
Correct: detection/selection/hash/history are cheap structural helpers in the existing crossModelReviewer module (the registry's documented extension point); the SKILL owns the flow policy; nothing re-implements the provider factory, circuit breaker, or aggregation (`aggregateRoundOutcomes` unchanged and still computes the spec-level flag).
|
|
41
|
+
|
|
42
|
+
## 4. Signal vs authority compliance
|
|
43
|
+
|
|
44
|
+
**Required reference:** docs/signal-vs-authority.md
|
|
45
|
+
|
|
46
|
+
- [x] No — no block/allow surface over agent behavior or messages. The two refusal points (allowlist, canary) are **hard-invariant validation** over enumerable sets (the principle's explicitly-allowed class — egress protection is the "safety guards on irreversible actions" case). Review outcomes only ever DEGRADE with logged reasons; judgment stays with the reviewers and the convergence process.
|
|
47
|
+
|
|
48
|
+
## 5. Interactions
|
|
49
|
+
|
|
50
|
+
- **Back-compat:** `detectCrossModelReviewer` (first-match) and the script's default single-pass mode are unchanged; existing callers/tests pass (hermeticity fixes only — the suite previously assumed gemini absent, false on hosts with gemini installed).
|
|
51
|
+
- **Aggregation:** per-family results feed the existing per-round → spec-level aggregation unchanged; a delta-skip is recorded as a logged note, not a round outcome, so `degraded-all-rounds` semantics are preserved.
|
|
52
|
+
- **Double-fire/races:** the activation JSONL is append-with-cap from a single skill flow; worst case under concurrent convergences is a lost observation line (history is a union-over-time read, so this cannot flip mandatory→optional within the lookback).
|
|
53
|
+
- **Circuit breakers:** each framework keeps its own breaker via the factory — a rate-limited codex degrades that pass; gemini still runs (the family-diversity goal).
|
|
54
|
+
|
|
55
|
+
## 6. External surfaces
|
|
56
|
+
|
|
57
|
+
- **LLM egress:** spec text now also flows to gemini (first-party Google OAuth CLI) when available — the same egress class as the existing codex pass, now explicitly allowlist-bounded. No new third-party endpoints; pi/custom endpoints structurally excluded.
|
|
58
|
+
- **Persistent state:** `state/framework-activation-history.jsonl` (capped). No fleet surface: spec-converge remains agent-private (decision recorded in the Piece-2 artifact); no `src/` runtime route/config change — `crossModelReviewer.ts` is library code consumed by the skill script.
|
|
59
|
+
|
|
60
|
+
## 7. Rollback cost
|
|
61
|
+
|
|
62
|
+
Revert the commit. The history file is inert if orphaned. No migration, no API, no fleet propagation.
|
|
63
|
+
|
|
64
|
+
## Conclusion
|
|
65
|
+
|
|
66
|
+
Piece 3 closes the spec: externals can no longer be quietly skipped (durable 7-day baseline + delta-gating with logged notes), selection is dynamic and family-diverse with zero pinned model names, and the two real security findings from convergence (spec egress to untrusted endpoints; silent dead-model reviews) are structurally closed (allowlist + canary). Verified: tsc clean, full lint clean, 77 unit (45 existing + 32 new) + 3 integration green, live smoke on this host detected both codex + gemini and refused pi-cli. Clear to ship as the final PR of the spec.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Scope disclosure (honest residual)
|
|
71
|
+
|
|
72
|
+
Two spec clauses are NOT implemented in this PR and are tracked, not orphaned <!-- tracked: CMT-1317 -->: (a) the Claude-only-agent floor reaching a *different-family* model via the subscription-path/provider-registry fallback — a single-framework agent currently gets the honest advisory `unavailable` flag (the pre-existing behavior, loudly disclosed in the report banner), and (b) surfacing "no external family reachable at all" as a HumanAsDetector-style tracked-gap signal rather than the advisory note. Both need provider-registry plumbing that doesn't belong in this hardening pass.
|
|
73
|
+
|
|
74
|
+
## Second-pass review
|
|
75
|
+
|
|
76
|
+
**Reviewer:** independent reviewer subagent (verified findings against the actually-installed gemini CLI v0.25.2).
|
|
77
|
+
**Independent read of the artifact: concern raised → all resolved this pass.**
|
|
78
|
+
|
|
79
|
+
- MUST-FIX (resolved): `GEMINI_HOME` is not a real gemini CLI env var (zero occurrences in the CLI dist; creds are unconditionally `~/.gemini/oauth_creds.json`). Honoring it made detection probe a path the CLI never reads — a false-unavailable would silently skip the gemini pass AND poison the activation baseline with `gemini-cli:false`, the exact suppression Piece 3 prevents. Fixed: env lookup dropped; injectable test seam kept.
|
|
80
|
+
- NICE-TO-HAVE (applied): frontmatter-strip close anchor tightened to a whole-line fence (`\n---(\n|$)`) so `--- text`/`----` inside the block can't terminate the strip mid-line.
|
|
81
|
+
- INFORMATIONAL (applied anyway): `wasNonClaudeFrameworkActiveWithin` now counts only TRUSTED reviewer framework ids, so a stray `{"claude-code":true}` line can't flip the externals-mandatory decision.
|
|
82
|
+
- HONESTY (applied): the scope disclosure above was added at the reviewer's prompting — the artifact previously implied full Piece-3 coverage.
|
|
83
|
+
- Confirmed clean: `--family` allowlist guard sits before any detect/provider call; no path (default, detect-all, or detectionOverride) can route spec text to a non-registry framework — the registry⊆allowlist invariant is itself unit-tested; `buildIntelligenceProvider({framework:'gemini-cli'})` cannot reach a custom base-URL (allowlisted child env); refresh-token-only IS authed for this CLI; the canary precedes provider construction in both entries; the 2000-line cap keeps the most recent lines; the reader never throws on corruption; the existing-test edits weaken no assertion.
|
|
84
|
+
|
|
85
|
+
After fixes: tsc exit 0, 77/77 unit + 3/3 integration green.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Evidence pointers
|
|
90
|
+
|
|
91
|
+
- `tests/unit/crossModelReviewer-piece3.test.ts` (32): gemini detect triad, detect-all cardinality, canary accept/reject, hash frontmatter/CRLF stability, activation write/read/lookback/corrupt-line/cap, allowlist, gemini degraded paths.
|
|
92
|
+
- Live smoke: `--detect-only --state-dir` → codex-cli:gpt-5.5 + gemini-cli:gemini-2.5-pro both detected + observation written; `--family pi-cli` → `untrusted-framework`; `--hash-only` → stable hash.
|
|
93
|
+
- `npx tsc --noEmit` exit 0; `npm run lint` exit 0.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Side-Effects Review — Decision-Completeness Gate in spec-converge (Autonomy Principles Enforcement, Piece 2)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `decision-completeness-gate`
|
|
4
|
+
**Date:** `2026-06-10`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `independent reviewer subagent — concern raised, all items resolved (see below)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Implements Piece 2 of `docs/specs/AUTONOMY-PRINCIPLES-ENFORCEMENT-SPEC.md`: makes single-run-completability **provable** in spec-converge. Three parts:
|
|
11
|
+
|
|
12
|
+
1. **New internal reviewer** (`templates/reviewer-decision-completeness.md`, 6th of six): enumerates every mid-run stop-and-ask-the-user point; each must be **frontloaded** into `## Frontloaded Decisions` or tagged **cheap-to-change-after** behind a named dark/dry-run/read-only phase. The reviewer **CONTESTS every cheap tag** against a closed non-cheap taxonomy (durable external side-effects, money, identity, published/user-visible interface — NEVER cheap); a rejected tag is a material finding that blocks convergence.
|
|
13
|
+
2. **New convergence criterion** (SKILL.md Phase 3): a spec cannot converge while `## Open questions` contains an unresolved user-decision — additive to "no material new issues."
|
|
14
|
+
3. **Structural enforcement + earned evidence** (`write-convergence-tag.mjs`): the tag writer now REFUSES to stamp `review-convergence` while open questions remain (criterion 2 cannot be skipped by prose), and writes `single-run-completable: true` + the reviewer's counts (`frontloaded-decisions`, `cheap-to-change-tags`, `contested-then-cleared`) so the tag carries its evidence — earned, not minted. The script gained an import-safe main guard so the parser (`findOpenQuestions`) is unit-testable.
|
|
15
|
+
|
|
16
|
+
Files: `skills/spec-converge/SKILL.md`, `skills/spec-converge/templates/reviewer-decision-completeness.md` (new), `skills/spec-converge/scripts/write-convergence-tag.mjs`, `tests/unit/write-convergence-tag-decision-completeness.test.ts` (new, 15 tests).
|
|
17
|
+
|
|
18
|
+
## Decision-point inventory
|
|
19
|
+
|
|
20
|
+
- `write-convergence-tag.mjs` open-questions gate — **add** — a deterministic, commit-time text validator: refuses the convergence stamp while `## Open questions` has unresolved entries. (Hard-invariant class, see §4.)
|
|
21
|
+
- Convergence criterion 2 (SKILL.md) — **add** — prose criterion backed by the structural gate above.
|
|
22
|
+
- Decision-Completeness reviewer — **add** — produces findings (signals) folded into the round; blocking authority remains the convergence process itself.
|
|
23
|
+
- `single-run-completable` frontmatter — **add** — disclosure only; does NOT change /instar-dev's `review-convergence` + `approved` enforcement.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 1. Over-block
|
|
28
|
+
|
|
29
|
+
The open-questions gate could refuse a spec whose `## Open questions` section contains commentary that *looks* unresolved. Mitigated: blockquote lines (`>`), none-markers (`*(none)*`, `(none)`, `None`, `None.`, `N/A`, emphasis variants), blank lines, and horizontal rules are all recognized as resolved; only contentful entries refuse. Unit-tested per variant. Residual: a spec author writing prose commentary as plain (non-blockquote) text under Open questions gets refused — acceptable, the error message names the fix and the convention (blockquote commentary) is already what existing specs use (this very spec's Piece-1 sibling used `> Per Principle 2…` + `*(none)*`, which passes).
|
|
30
|
+
|
|
31
|
+
## 2. Under-block
|
|
32
|
+
|
|
33
|
+
Two honest gaps, both by design: (a) the gate validates the SECTION, not the whole document — a user-decision buried in prose outside `## Open questions` is the REVIEWER's job to find (LLM judgment), not the deterministic gate's; (b) an author could delete the question instead of resolving it — but the Decision-Completeness reviewer re-reads the full spec every round and the deleted-but-unresolved decision resurfaces as a finding (the same "rewriting the spec to hide findings" anti-pattern the skill already documents and catches).
|
|
34
|
+
|
|
35
|
+
## 3. Level-of-abstraction fit
|
|
36
|
+
|
|
37
|
+
Correct: the deterministic part (is the section empty?) is a cheap structural validator at the tag-writer boundary — the same layer as the existing ELI16-presence check it sits beside. The judgment part (is this REALLY cheap-to-change-after? is that REALLY the user's decision?) lives in the LLM reviewer. Neither re-implements the other.
|
|
38
|
+
|
|
39
|
+
## 4. Signal vs authority compliance
|
|
40
|
+
|
|
41
|
+
**Required reference:** docs/signal-vs-authority.md
|
|
42
|
+
|
|
43
|
+
- [x] No — the new blocking surface is **hard-invariant validation at a tool boundary**, not a judgment gate.
|
|
44
|
+
|
|
45
|
+
The open-questions gate is the "structural validators at the boundary of the system" case the principle explicitly allows (like "this field must be a number"): it checks section emptiness against an enumerable marker set — zero judgment, deterministic, with a clear remediation message. The *judgment* calls (contesting cheap tags, finding buried decisions) are the reviewer's — and the reviewer produces findings (signals) that block only through the existing convergence process, exactly like the other five reviewers. No brittle check gained judgment authority.
|
|
46
|
+
|
|
47
|
+
## 5. Interactions
|
|
48
|
+
|
|
49
|
+
- **Shadowing:** the gate runs after the ELI16 check inside the same script — ordering is irrelevant (both must pass; neither consumes the other's input).
|
|
50
|
+
- **Existing callers:** `write-convergence-tag.mjs` is invoked only by the spec-converge skill flow. Its existing tests (`write-convergence-tag-crossmodel.test.ts`, 5 tests) pass unchanged — the new args are optional and the main-guard restructure preserves CLI behavior (IS_MAIN hardened with realpath + fileURLToPath after second-pass review so a symlinked or %-encoded invocation cannot silently no-op).
|
|
51
|
+
- **Pre-existing converged specs:** unaffected — the gate fires at stamp time only; already-stamped specs are never re-validated. Specs converged before this ships simply lack `single-run-completable` (honest, documented in SKILL.md).
|
|
52
|
+
- **Idempotency:** re-runs strip and rewrite the new fields exactly like the review-* chain (tested).
|
|
53
|
+
|
|
54
|
+
## 6. External surfaces
|
|
55
|
+
|
|
56
|
+
None at runtime. This is skill-content + a repo script + tests: no `src/` change, no API, no fleet migration surface (spec-converge is **agent-private** — deliberately NOT in the builtin skill set, matching `/instar-dev`'s explicit not-user-facing status; the spec's "promote vs declare agent-private" decision is resolved as **agent-private**, recorded here). Future instar-dev specs converge under the stricter criteria — that is the intended effect.
|
|
57
|
+
|
|
58
|
+
## 7. Rollback cost
|
|
59
|
+
|
|
60
|
+
Trivial: revert the commit. No persistent state, no migration, no user-visible runtime surface. Specs stamped in the interim keep their earned fields (harmless disclosure).
|
|
61
|
+
|
|
62
|
+
## Conclusion
|
|
63
|
+
|
|
64
|
+
Piece 2 lands the spec's design with the criterion enforced structurally (Structure > Willpower) rather than as prose: the tag writer is now the chokepoint that makes "a spec cannot converge with open user-decisions" unskippable, and the `single-run-completable` tag carries its evidence counts. The one new blocking surface is hard-invariant validation, explicitly inside the principle's allowed class. Verified by 15 new unit tests + the 16 existing cross-model tag tests + live functional runs (refuses on a live question; stamps with counts once resolved). Clear to ship as PR 3-of-3's sibling (PR 2).
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Second-pass review
|
|
69
|
+
|
|
70
|
+
**Reviewer:** independent reviewer subagent (adversarial audit of artifact + code + tests, ran the suites).
|
|
71
|
+
**Independent read of the artifact: concern raised → all resolved this pass.**
|
|
72
|
+
|
|
73
|
+
- MUST-FIX (resolved): the IS_MAIN guard could silently exit 0 (doing nothing) when invoked via a symlink or a %-encodable path — safe direction (tag never written without the checks) but a fail-loud violation. Fixed: realpathSync + fileURLToPath comparison; the pre-existing `ROOT` URL-pathname decode bug rode the same fix.
|
|
74
|
+
- MUST-FIX (resolved): this artifact overstated the existing cross-model test count (claimed 16; vitest counts 5). Corrected above.
|
|
75
|
+
- NICE-TO-HAVE (applied): heading-variant false-pass (`## Open questions (round 2)` was invisible to the gate) — regex loosened to `\b[^\n]*$`.
|
|
76
|
+
- NICE-TO-HAVE (applied): SKILL.md now scopes the "earned" claim honestly (the structural guarantee is the open-questions invariant; the counts are caller-supplied disclosure on the same trust model as `--cross-model-review`) and names the blockquote-commentary convention's limit with its reviewer backstop.
|
|
77
|
+
- Confirmed clean: signal-vs-authority compliance (deterministic hard-invariant validation; all judgment in the LLM reviewer), taxonomy fidelity to the spec, and test honesty (refuse → exit 1 AND tag absent; idempotency).
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Evidence pointers
|
|
82
|
+
|
|
83
|
+
- `tests/unit/write-convergence-tag-decision-completeness.test.ts` — 15 tests: parser none-marker variants, section-scoping, refuse-on-live-question (and tag NOT written), stamp-on-resolved, earned counts, no-counts → no minted tag, idempotent re-runs.
|
|
84
|
+
- `tests/unit/write-convergence-tag-crossmodel.test.ts` — 16 existing tests green (no regression from the main-guard restructure).
|
|
85
|
+
- Live functional run: refused `- **Q1:** should we do A or B?` with exit 1 + remediation message; stamped `single-run-completable: true` + counts on the `*(none)*` variant.
|