@gajae-code/coding-agent 0.11.11 → 0.12.0
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/CHANGELOG.md +49 -3
- package/dist/types/cli/read-cli.d.ts +1 -0
- package/dist/types/commands/deep-interview.d.ts +9 -0
- package/dist/types/commands/harness.d.ts +22 -0
- package/dist/types/commands/read.d.ts +6 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/provider-auth-health.d.ts +14 -0
- package/dist/types/config/provider-ranking.d.ts +59 -0
- package/dist/types/config/settings-schema.d.ts +29 -1
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/extensibility/extensions/types.d.ts +22 -2
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +5 -12
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +17 -32
- package/dist/types/gjc-runtime/deep-interview-stage.d.ts +37 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +4 -135
- package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/memory-guard-owner-claims.d.ts +47 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +52 -1
- package/dist/types/gjc-runtime/state-writer.d.ts +5 -46
- package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +2 -1
- package/dist/types/gjc-runtime/team-store.d.ts +12 -0
- package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +92 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +7 -0
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +8 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -5
- package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
- package/dist/types/main.d.ts +1 -1
- package/dist/types/memories/index.d.ts +2 -0
- package/dist/types/modes/acp/acp-agent.d.ts +8 -0
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
- package/dist/types/modes/components/read-tool-group.d.ts +4 -4
- package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/controllers/event-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
- package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +5 -0
- package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
- package/dist/types/sdk/bus/index.d.ts +15 -1
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +2 -1
- package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
- package/dist/types/sdk/host/control/operations.d.ts +1 -1
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/host/query/handlers.d.ts +5 -0
- package/dist/types/session/agent-session.d.ts +44 -10
- package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
- package/dist/types/session/session-manager.d.ts +25 -13
- package/dist/types/session/streaming-output.d.ts +60 -5
- package/dist/types/skill-state/active-state.d.ts +0 -4
- package/dist/types/skill-state/workflow-hud.d.ts +3 -0
- package/dist/types/tools/browser/launch.d.ts +7 -0
- package/dist/types/tools/fetch.d.ts +17 -1
- package/dist/types/tools/output-meta.d.ts +21 -1
- package/dist/types/tools/read-internals.d.ts +12 -0
- package/dist/types/tools/read.d.ts +45 -1
- package/dist/types/tools/resource-gc.d.ts +4 -0
- package/dist/types/tools/tool-result.d.ts +7 -1
- package/package.json +9 -13
- package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
- package/scripts/capture-sticky-viewport-showcase.ts +292 -0
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/scripts/verify-gjc-sdk-canonicalization.ts +122 -18
- package/scripts/verify-sticky-viewport-showcase.ts +611 -0
- package/src/capability/index.ts +8 -0
- package/src/cli/read-cli.ts +2 -1
- package/src/cli.ts +1 -0
- package/src/commands/deep-interview.ts +29 -9
- package/src/commands/harness.ts +35 -12
- package/src/commands/read.ts +11 -2
- package/src/commands/team.ts +1 -1
- package/src/config/model-registry.ts +21 -2
- package/src/config/provider-auth-health.ts +42 -0
- package/src/config/provider-ranking.ts +119 -0
- package/src/config/settings-schema.ts +23 -1
- package/src/config/settings.ts +246 -108
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +229 -82
- package/src/defaults/gjc/skills/ralplan/SKILL.md +58 -25
- package/src/defaults/gjc/skills/team/SKILL.md +1 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +25 -15
- package/src/eval/py/executor.ts +7 -2
- package/src/extensibility/extensions/types.ts +28 -4
- package/src/extensibility/shared-events.ts +5 -0
- package/src/gjc-runtime/deep-interview-ambiguity.ts +47 -82
- package/src/gjc-runtime/deep-interview-recorder.ts +227 -310
- package/src/gjc-runtime/deep-interview-runtime.ts +109 -270
- package/src/gjc-runtime/deep-interview-stage.ts +1016 -0
- package/src/gjc-runtime/deep-interview-state.ts +12 -943
- package/src/gjc-runtime/gc-render.ts +1 -0
- package/src/gjc-runtime/gc-runtime.ts +11 -1
- package/src/gjc-runtime/memory-guard-owner-claims.ts +389 -0
- package/src/gjc-runtime/ralplan-runtime.ts +742 -85
- package/src/gjc-runtime/state-runtime.ts +2 -1
- package/src/gjc-runtime/state-writer.ts +70 -222
- package/src/gjc-runtime/team-launch.ts +34 -5
- package/src/gjc-runtime/team-runtime.ts +970 -33
- package/src/gjc-runtime/team-store.ts +32 -0
- package/src/gjc-runtime/team-worker-memory-guard.ts +363 -0
- package/src/gjc-runtime/team-workers.ts +1 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +18 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +20 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +13 -7
- package/src/gjc-runtime/workflow-manifest.generated.json +97 -350
- package/src/gjc-runtime/workflow-manifest.ts +34 -179
- package/src/hooks/native-skill-hook.ts +17 -4
- package/src/hooks/skill-state.ts +1 -23
- package/src/internal-urls/docs-index.generated.ts +8 -9
- package/src/internal-urls/local-root-gc.ts +127 -0
- package/src/main.ts +9 -5
- package/src/memories/index.ts +10 -0
- package/src/modes/DESIGN.md +47 -0
- package/src/modes/acp/acp-agent.ts +43 -7
- package/src/modes/acp/acp-event-mapper.ts +14 -7
- package/src/modes/acp/acp-mode.ts +39 -3
- package/src/modes/components/assistant-message.ts +123 -22
- package/src/modes/components/irc-sidebar.ts +176 -62
- package/src/modes/components/model-selector.ts +61 -4
- package/src/modes/components/oauth-selector.ts +42 -12
- package/src/modes/components/read-tool-group.ts +82 -14
- package/src/modes/components/session-observer-overlay.ts +187 -32
- package/src/modes/components/settings-selector.ts +60 -18
- package/src/modes/components/tool-execution.ts +138 -27
- package/src/modes/controllers/event-controller.ts +109 -27
- package/src/modes/controllers/extension-ui-controller.ts +19 -2
- package/src/modes/controllers/input-controller.ts +18 -3
- package/src/modes/controllers/selector-controller.ts +83 -37
- package/src/modes/interactive-mode.ts +62 -3
- package/src/modes/irc-observation-ledger.ts +71 -15
- package/src/modes/runtime-init.ts +19 -2
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/ui-helpers.ts +94 -1
- package/src/prompts/agents/architect.md +9 -1
- package/src/prompts/agents/critic.md +9 -0
- package/src/prompts/system/system-prompt.md +0 -1
- package/src/prompts/tools/read.md +4 -2
- package/src/runtime-mcp/smithery-auth.ts +17 -2
- package/src/runtime-mcp/smithery-connect.ts +11 -1
- package/src/sdk/broker/lifecycle.ts +25 -6
- package/src/sdk/bus/control-drain-lease.ts +104 -0
- package/src/sdk/bus/index.ts +341 -38
- package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
- package/src/sdk/bus/prompt-reconciliation.ts +2 -1
- package/src/sdk/bus/reconciliation-store.ts +224 -0
- package/src/sdk/host/control/dispatch.ts +5 -1
- package/src/sdk/host/control/operations.ts +1 -1
- package/src/sdk/host/host.ts +23 -1
- package/src/sdk/host/query/handlers.ts +55 -0
- package/src/sdk/mcp/server.ts +46 -18
- package/src/sdk/protocol/operation-inventory.generated.json +29 -0
- package/src/sdk/protocol/operation-registry.ts +7 -2
- package/src/sdk/session.ts +13 -1
- package/src/session/agent-session.ts +466 -50
- package/src/session/memory-guard-checkpoint-participant.ts +131 -0
- package/src/session/session-manager.ts +764 -17
- package/src/session/streaming-output.ts +262 -43
- package/src/setup/provider-onboarding.ts +19 -4
- package/src/skill-state/active-state.ts +0 -10
- package/src/skill-state/workflow-hud.ts +25 -2
- package/src/skill-state/workflow-mutation-guard.ts +65 -7
- package/src/tools/ask.ts +89 -69
- package/src/tools/browser/launch.ts +41 -6
- package/src/tools/fetch.ts +114 -23
- package/src/tools/output-meta.ts +176 -9
- package/src/tools/path-utils.ts +5 -1
- package/src/tools/read-internals.ts +44 -0
- package/src/tools/read.ts +1542 -310
- package/src/tools/resource-gc.ts +182 -18
- package/src/tools/tool-result.ts +14 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/dist/types/gjc-runtime/deep-interview-draft.d.ts +0 -8
- package/dist/types/gjc-runtime/deep-interview-payload.d.ts +0 -41
- package/dist/types/gjc-runtime/deep-interview-repair.d.ts +0 -17
- package/src/gjc-runtime/deep-interview-draft.ts +0 -843
- package/src/gjc-runtime/deep-interview-payload.ts +0 -262
- package/src/gjc-runtime/deep-interview-repair.ts +0 -1674
|
@@ -54,7 +54,7 @@ RECEIPT-ONLY guideline: role agents (`planner`, `architect`, and `critic`) persi
|
|
|
54
54
|
This skill runs GJC planning in consensus mode for the provided arguments.
|
|
55
55
|
|
|
56
56
|
The consensus workflow:
|
|
57
|
-
1. **Planner** creates the initial plan and a compact **RALPLAN-DR summary** before review. Launch the Planner ONCE per run as a detached, resumable subagent (await it before the Architect) and record its returned subagent id as the run's persisted Planner id; persist the stage with `gjc ralplan --write --stage planner --stage_n 1 --artifact-env GJC_RALPLAN_ARTIFACT --planner-id <id> --planner-resumable <true|false>` (see **Persisted
|
|
57
|
+
1. **Planner** creates the initial plan and a compact **RALPLAN-DR summary** before review. Launch the Planner ONCE per run as a detached, resumable subagent (await it before the Architect) and record its returned subagent id as the run's persisted Planner id; persist the stage with `gjc ralplan --write --stage planner --stage_n 1 --artifact-env GJC_RALPLAN_ARTIFACT --planner-id <id> --planner-resumable <true|false>` (see **Persisted role agents** below):
|
|
58
58
|
- After persistence, return only the receipt/path plus compact planning status; do not paste the full plan markdown back to the caller unless explicitly requested.
|
|
59
59
|
- Principles (3-5)
|
|
60
60
|
- Decision Drivers (top 3)
|
|
@@ -62,15 +62,26 @@ The consensus workflow:
|
|
|
62
62
|
- If only one viable option remains, explicit invalidation rationale for alternatives
|
|
63
63
|
- Deliberate mode only: pre-mortem (3 scenarios) + expanded test plan (unit/integration/e2e/observability)
|
|
64
64
|
2. **User feedback** *(--interactive only)*: If `--interactive` is set, use the `ask` tool to present the draft plan **plus the Principles / Drivers / Options summary** before review (Proceed to review / Request changes / Skip review). Otherwise, automatically proceed to review.
|
|
65
|
-
3. **Review fan-out after Planner persistence**: launch
|
|
66
|
-
- **Architect lane**: challenge architecture, surface tradeoff tensions, and enrich thin plans with synthesis or missed sub-scope. Persist with `gjc ralplan --write --stage architect --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json`, then return receipt/path plus `CLEAR`/`WATCH`/`BLOCK` and `APPROVE`/`COMMENT`/`REQUEST CHANGES`.
|
|
67
|
-
- **Plan-only Critic lane**: independently check quality, principle-option consistency, alternatives, risks, acceptance criteria, and verification; when the plan is thin, request concrete expansion rather than only defects. Persist with `gjc ralplan --write --stage critic --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json`, then return receipt/path plus `OKAY`/`ITERATE`/`REJECT`.
|
|
65
|
+
3. **Review fan-out after Planner persistence**: launch the Architect and Critic ONCE per run as detached, resumable review lanes against the same immutable Planner receipt/path/sha/stage_n. Their pass-1 fan-out remains parallel when Critic is **plan-only** and does not consume Architect output (see **Persisted role agents** below).
|
|
66
|
+
- **Architect lane**: challenge architecture, surface tradeoff tensions, and enrich thin plans with synthesis or missed sub-scope. Persist with `gjc ralplan --write --stage architect --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --architect-id <id> --architect-resumable <true|false> --lane-verdict <token> --json`, then return receipt/path plus `CLEAR`/`WATCH`/`BLOCK` and `APPROVE`/`COMMENT`/`REQUEST CHANGES`.
|
|
67
|
+
- **Plan-only Critic lane**: independently check quality, principle-option consistency, alternatives, risks, acceptance criteria, and verification; when the plan is thin, request concrete expansion rather than only defects. Persist with `gjc ralplan --write --stage critic --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --critic-id <id> --critic-resumable <true|false> --lane-verdict <token> --json`, then return receipt/path plus `OKAY`/`ITERATE`/`REJECT`.
|
|
68
68
|
- **Sequential fallback**: if Critic must evaluate Architect findings, verdict, antithesis, tradeoffs, synthesis, status, or any Architect-produced artifact, await the Architect result before issuing that Architect-dependent Critic pass.
|
|
69
|
+
- Every Architect/Critic assignment, including each pass-2+ re-review assignment in step 5, MUST instruct the reviewer to include `--lane-verdict <token>` on its existing `gjc ralplan --write`: Architect passes its Architectural Status token (`CLEAR`/`WATCH`/`BLOCK`), and Critic passes its verdict token (`OKAY`/`ITERATE`/`REJECT`). The flag is optional so legacy invocations stay valid.
|
|
69
70
|
4. **Review join gate**: before consensus, revision, reconciliation, finalization, or approval, verify both Architect and Critic receipts/verdicts exist for the same Planner artifact/pass (`path`, `sha256`, `stage_n`). A non-`CLEAR` Architect verdict, non-`APPROVE` Architect decision, or any non-`OKAY` Critic verdict routes back to Planner revision; do not finalize from only one review lane.
|
|
70
|
-
5. **Re-review loop** (max 5 iterations; **runtime-enforced**): Any non-`OKAY` Critic verdict (`ITERATE` or `REJECT`) or Architect result that is not `CLEAR`/`APPROVE` MUST run the same full closed loop:
|
|
71
|
+
5. **Re-review loop** (max 5 iterations; **runtime-enforced**): Any non-`OKAY` Critic verdict (`ITERATE` or `REJECT`) or Architect result that is not `CLEAR`/`APPROVE` MUST run the same full closed loop. Pass 2+ resumes the SAME persisted Architect and Critic lane subagents with the mandatory re-review context bundle and runs sequentially Architect -> Critic: await the Architect result and its receipt/path before assigning Critic; Critic receives the current-pass Architect receipt/path and performs the rule-5 counter-review before consolidated feedback routes to Planner revision. From pass 2, both reviewers are bound by the five-rule ratchet: delta-only review, novelty justification, verdict monotonicity, severity scoping, and Critic counter-review of Architect scope inflation; unjustified inflation does not force a revision.
|
|
71
72
|
a. Collect Architect + Critic feedback
|
|
72
|
-
b. Revise the plan by resuming the SAME persisted Planner subagent with consolidated Architect + Critic feedback (see **Persisted
|
|
73
|
-
|
|
73
|
+
b. Revise the plan by resuming the SAME persisted Planner subagent with consolidated Architect + Critic feedback (see **Persisted role agents** below); fall back to a fresh Planner spawn only per the fallback routing table
|
|
74
|
+
|
|
75
|
+
**Re-review context bundle (pass 2+; mandatory):** Every pass-2+ Architect or Critic assignment MUST include:
|
|
76
|
+
1. the explicit review pass number `N` for that lane, stated literally as `review pass N` in the assignment text, where **N is the ordinal review pass for that lane across the entire ralplan run/re-review loop** (equivalently the opener-iteration ordinal): the review of the initial Planner artifact is `review pass 1`, the review of the first revised Planner artifact is `review pass 2`, and so on; **N never resets within an opener iteration and never resets when a new `revision` opener begins in the same run** — it increments monotonically with every review the lane performs in the run. This ordinal is a workflow counter distinct from the runtime lane budget (which counts lane writes per opener iteration, WI-5): at the default budget the two coincide numerically, but the ratchet ("from pass 2") always keys off the run-level N so normal post-revision re-reviews activate delta-only review, monotonicity, and the sequential cadence;
|
|
77
|
+
2. the current revision receipt under review (`path`, `sha256`, `stage_n`);
|
|
78
|
+
3. the prior Planner/revision artifact path that the previous pass reviewed;
|
|
79
|
+
4. the prior same-lane review artifact path (`stage-NN-architect.md` / `stage-NN-critic.md`) with its receipt fields;
|
|
80
|
+
5. the consolidated prior blockers and the revision's claimed resolutions, as orchestrator-collected pointers into those artifacts (never pasted bodies);
|
|
81
|
+
6. Critic pass-2+ only: the current-pass Architect receipt/path, awaited first per the sequential cadence, so the rule-5 counter-review is evaluable.
|
|
82
|
+
|
|
83
|
+
**The re-review context bundle remains mandatory regardless of whether a reviewer is resumed or uses a fresh-spawn fallback.** A fresh-spawn fallback always receives everything required to apply delta-only review (rule 1), novelty justification (rule 2), monotonicity (rule 3), severity scoping (rule 4), and counter-review (rule 5).
|
|
84
|
+
c. For pass 2+, resume (or fresh-spawn only per the routing table) Architect -> Critic sequentially: await the Architect result and receipt/path, then issue Critic with the mandatory context bundle, including the current-pass Architect receipt/path. Critic performs the rule-5 counter-review before consolidated feedback routes to Planner revision.
|
|
74
85
|
- Persist each Planner revision with `gjc ralplan --write --stage revision --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json` before re-review, then pass the receipt/path forward instead of duplicating the full revision markdown in the parent conversation.
|
|
75
86
|
d. Re-join Architect and Critic verdicts for the same revised Planner artifact/pass
|
|
76
87
|
e. Repeat this loop until Critic returns `OKAY` **and** Architect is `CLEAR`/`APPROVE` for the same Planner artifact/pass, or 5 iterations are reached
|
|
@@ -102,7 +113,7 @@ The consensus workflow:
|
|
|
102
113
|
|
|
103
114
|
The skill tool then dispatches the execution skill same-turn and runs `gjc state ralplan handoff --to <team|ultragoal> --json` in-process to atomically demote ralplan, promote the callee, and sync `.gjc/_session-{sessionid}/state/skill-active-state.json`. You do not need to run the handoff verb yourself.
|
|
104
115
|
|
|
105
|
-
> **Important:** Architect and Critic MAY run in the same parallel batch only for the plan-only Critic lane after Planner persistence.
|
|
116
|
+
> **Important:** Architect and Critic MAY run in the same parallel batch only for the plan-only Critic lane after Planner persistence (review pass 1). Pass 2+ re-reviews MUST run sequentially Architect -> Critic: await Architect before issuing Critic, pass the current-pass Architect receipt/path to Critic for the rule-5 counter-review, then apply the same review join gate before consensus.
|
|
106
117
|
|
|
107
118
|
## Consensus iteration cap (operator contract)
|
|
108
119
|
|
|
@@ -122,35 +133,57 @@ The consensus workflow:
|
|
|
122
133
|
}
|
|
123
134
|
```
|
|
124
135
|
|
|
136
|
+
## Per-lane review budget (operator contract)
|
|
137
|
+
|
|
138
|
+
- Default: **1** Architect pass and **1** Critic pass per opener iteration.
|
|
139
|
+
- Override via `gjc.ralplan.maxReviewPassesPerLane`: project `.gjc/settings.json` overrides user settings; the value is an integer **1..10** registered in the public settings schema.
|
|
140
|
+
- On overflow: exit code **3** with the **`PLANNING-STUCK`** marker and lane-specific JSON/stderr detail.
|
|
141
|
+
- `post-interview`, `adr`, and `final` are always allowed.
|
|
142
|
+
- Identical re-writes dedupe without stuck-signaling — including after a crash between artifact write and ledger append: the identical retry repairs the missing ledger row and returns the dedupe receipt.
|
|
143
|
+
- A new `--run-id` starts a fresh budget.
|
|
144
|
+
- A rule-2-justified blocker routes through a Planner `revision` opener (new iteration, fresh lane budget), never a second same-iteration review pass.
|
|
145
|
+
- Override example (project `.gjc/settings.json`):
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"gjc": {
|
|
150
|
+
"ralplan": {
|
|
151
|
+
"maxIterations": 3,
|
|
152
|
+
"maxReviewPassesPerLane": 2
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
125
158
|
|
|
126
159
|
Follow this ralplan-internal consensus workflow for consensus mode details.
|
|
127
160
|
|
|
128
|
-
### Persisted
|
|
161
|
+
### Persisted role agents (consensus loop)
|
|
129
162
|
|
|
130
|
-
The Planner
|
|
163
|
+
The Planner, Architect, and Critic are **same-session persisted subagents**. Launch the Planner detached once and await it before review fan-out; Architect and Critic are also launched once per run as detached, resumable subagents in the pass-1 fan-out (parallel only for the plan-only Critic lane tied to the same Planner receipt/path/sha/stage_n). On pass 2+, resume the SAME persisted Planner with consolidated feedback and resume the SAME persisted Architect and Critic lane subagents with the mandatory re-review context bundle instead of fresh-spawning. Do NOT modify the subagent control surface; use existing `subagent` resume/steer controls only.
|
|
131
164
|
|
|
132
|
-
**Persistence boundary:** same-parent, active-session continuity only. Resumability requires retained subagent resume metadata and a persistent parent session (in-memory parent yields `resumable:false`), not just `.gjc` run-state. A terminal subagent can still resume when its retained descriptor points at a saved subagent session; after process restart, missing metadata, or failed/unavailable resume, use fresh
|
|
165
|
+
**Persistence boundary:** same-parent, active-session continuity only. Resumability requires retained subagent resume metadata and a persistent parent session (in-memory parent yields `resumable:false`), not just `.gjc` run-state. A terminal subagent can still resume when its retained descriptor points at a saved subagent session; after process restart, missing metadata, or failed/unavailable resume, use the fresh role/lane fallback.
|
|
133
166
|
|
|
134
|
-
**Resume routing table** (per re-review pass, when resuming
|
|
167
|
+
**Resume routing table (for every persisted role: Planner, Architect, and Critic)** (per re-review pass, when resuming that role's persisted id):
|
|
135
168
|
|
|
136
169
|
| Resume outcome | Action |
|
|
137
170
|
|---|---|
|
|
138
|
-
| `running` | `steer`/inject
|
|
139
|
-
| `queued` | retain/update the queued message or await the same id — do NOT fresh-spawn just because it is queued |
|
|
140
|
-
| `context_unavailable`, `not_found`, `no_runner`, `resume_failed` | fresh
|
|
141
|
-
| terminal (`completed`/`failed`/`cancelled`) +
|
|
171
|
+
| `running` | `steer`/inject that role's follow-up context to the same id, then await — do NOT fresh-spawn |
|
|
172
|
+
| `queued` | retain/update the queued message or `await` the same id — do NOT fresh-spawn just because it is queued |
|
|
173
|
+
| `context_unavailable`, `not_found`, `no_runner`, `resume_failed` | fresh-spawn fallback for that role/lane on that pass; record the fallback metadata. `not_found` should only mean same-session resume metadata is unavailable, not merely that a terminal live job was evicted. |
|
|
174
|
+
| terminal (`completed`/`failed`/`cancelled`) + follow-up message | resume the same id when context is available; otherwise use the fresh-spawn fallback above |
|
|
142
175
|
|
|
143
|
-
**
|
|
176
|
+
**Ratchet synergy:** a resumed Architect or Critic natively retains prior-pass context, but the re-review context bundle remains mandatory regardless so the fresh-spawn fallback remains fully functional and applies all five rules.
|
|
144
177
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
178
|
+
**Recording persisted-role-agent metadata** (audit/routing only — never claim `subagent list` proves resumability, since the snapshot does not expose `resumable`). Ride the matching optional flags on the role's normal `--write` for the pass:
|
|
179
|
+
|
|
180
|
+
| Role | Normal write stage | Metadata flags |
|
|
181
|
+
|---|---|---|
|
|
182
|
+
| Planner | `planner` or `revision` | `--planner-id <id> --planner-resumable <true|false>` |
|
|
183
|
+
| Architect | `architect` | `--architect-id <id> --architect-resumable <true|false>` |
|
|
184
|
+
| Critic | `critic` | `--critic-id <id> --critic-resumable <true|false>` |
|
|
152
185
|
|
|
153
|
-
|
|
186
|
+
The existing fallback flags ride the same role's normal write: `--fallback-reason <context_unavailable|not_found|no_runner|resume_failed|process_restart|missing_record>`, `--fallback-attempted-id <id>`, `--fallback-stage-n <N>`, and optional `--fallback-receipt-path <fresh-role-stage-artifact-path>`. A planner/revision write records Planner fallback metadata, an Architect write records Architect fallback metadata, and a Critic write records Critic fallback metadata. Set the matching `--*-resumable` flag to `true` only when the parent session is provably persistent; set/record `false` after an observed `context_unavailable`; otherwise omit it (unknown). Fallback flags are recorded only when a fresh-spawn fallback actually occurs: a fallback record requires `--fallback-reason` **together with** `--fallback-attempted-id` and `--fallback-stage-n` (the failed id and the pass it failed on), while `--fallback-receipt-path` is optional.
|
|
154
187
|
|
|
155
188
|
## Pre-Execution Gate
|
|
156
189
|
|
|
@@ -307,7 +307,7 @@ GJC ports team-mode concepts from `../../oh-my-codex`, not code or OMX/Codex-spe
|
|
|
307
307
|
| Startup ACK | `gjc team api worker-startup-ack`, persisted as `workers/<worker>/startup-ack.json`. |
|
|
308
308
|
| Claim-safe lifecycle APIs | `claim-task`, `transition-task-status`, and `release-task-claim` with worker ownership and claim-token guards. |
|
|
309
309
|
| Delivery states and deferred pane attempts | Native notification records under `.gjc/_session-{sessionid}/state/team/<team>/notifications/` with `pending`, `sent`, `queued`, `deferred`, `failed`, `delivered`, and `acknowledged` states. |
|
|
310
|
-
|
|
|
310
|
+
| Opt-in memory-guard relaunch | Lifecycle nudges remain non-destructive by default. On Linux only, a worker whose durable `memory-guard.json` explicitly enables automatic action may be checkpointed and relaunched after sustained pressure, bounded retries, current claim validation, and a continuation-safe handoff; unsupported platforms and missing authority remain advisory-only. |
|
|
311
311
|
|
|
312
312
|
Forbidden assumptions: do not copy OMX paths, Codex notify payload formats, OMX process names, or source code directly. Keep tmux as the current runtime; native split-worker TUI remains roadmap-only.
|
|
313
313
|
|
|
@@ -178,28 +178,36 @@ Ultragoal execution should use GJC's bundled role-agent roster when a durable st
|
|
|
178
178
|
- Use `architect` for read-only architecture and code-review lanes, including `CLEAR` / `WATCH` / `BLOCK` status.
|
|
179
179
|
- Use `critic` for read-only plan or handoff critique before execution proceeds.
|
|
180
180
|
|
|
181
|
-
###
|
|
181
|
+
### Implementation delegation guidance
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
Direct inline implementation by the leader is the default. Delegate to `executor` subagents only when the expected diffs land in **genuinely different sub-domains, modules, or systems** — separable surfaces with independent acceptance criteria and no shared-file contention. File count or line count alone does not force delegation; a large change confined to one domain/subsystem is usually better done inline or by a single sequenced `executor`.
|
|
184
184
|
|
|
185
|
-
|
|
185
|
+
Delegation is worth it when:
|
|
186
186
|
|
|
187
|
-
-
|
|
188
|
-
-
|
|
189
|
-
-
|
|
190
|
-
- The leader has already made **2+ inline edit passes** on the same story and implementation is still materially incomplete.
|
|
187
|
+
- The story spans **multiple distinct sub-domains / modules / systems** (e.g. a CLI surface plus an unrelated runtime subsystem plus docs tooling) whose slices can proceed in parallel without coordinating on the same files.
|
|
188
|
+
- Each slice can be bounded with explicit targets and acceptance criteria that are verifiable independently of the other slices.
|
|
189
|
+
- The leader's checkpoint/verification duties would otherwise be crowded out by juggling unrelated domains inline.
|
|
191
190
|
|
|
192
|
-
|
|
191
|
+
When delegating:
|
|
193
192
|
|
|
194
|
-
-
|
|
195
|
-
-
|
|
196
|
-
-
|
|
197
|
-
- Small, atomic, single-file changes below these thresholds stay with the leader — do not over-delegate trivial work.
|
|
193
|
+
- Give each `executor` bounded targets and explicit acceptance criteria, and keep checkpoint/goal-state ownership in the leader.
|
|
194
|
+
- Parallelize only across genuinely different sub-domains/modules/systems; sequence anything with a real dependency or shared-surface overlap.
|
|
195
|
+
- Work within a single domain/subsystem stays with the leader as direct edits — do not split one cohesive change across subagents, and do not over-delegate trivial work.
|
|
198
196
|
- After integrating delegated slices, run `architect` / `critic` review lanes; worker agents never mutate `.gjc/_session-{sessionid}/ultragoal` or call goal tools.
|
|
199
197
|
|
|
200
198
|
When delegating with native subagents, an await timeout only limits the leader's wait. It is not subagent failure evidence and must not be used as a cancellation reason; inspect or continue independent work, and cancel only when the subagent has actually failed, gone off-track, or become unrecoverably wrong.
|
|
201
199
|
|
|
202
|
-
|
|
200
|
+
### Subagent reuse and resumption (token efficiency)
|
|
201
|
+
|
|
202
|
+
Fresh spawns re-pay the full context ramp-up (file reads, domain orientation, contract restatement) on every delegation. When a later slice or lane targets the **same sub-domain/module/system** as a prior subagent of the same role, **resume the prior subagent instead of freshly spawning**:
|
|
203
|
+
|
|
204
|
+
- Track the subagent id per role + domain as it is created; on the next same-domain `executor` slice or same-scope `architect` review lane, resume that id and inject only the delta (new targets, new acceptance criteria, the updated frozen change set) rather than re-briefing from scratch.
|
|
205
|
+
- Reuse is domain-scoped: resume only when the prior context is an asset. A slice in a genuinely different sub-domain/module/system gets a fresh spawn — stale cross-domain context is a liability, not a saving.
|
|
206
|
+
- Resumability requires retained subagent resume metadata and a persistent parent session; use existing `subagent` resume/steer controls only. Route per attempt: `running` → steer/inject to the same id and await; `queued` → retain or await the same id; terminal (`completed`/`failed`/`cancelled`) with context available → resume the same id; `context_unavailable`, `not_found`, `no_runner`, or `resume_failed` → fresh spawn fallback for that slice.
|
|
207
|
+
- A resumed subagent is still the same worker under the same contract: it must not mutate `.gjc/_session-{sessionid}/ultragoal`, call goal tools, or absorb checkpoint/goal-state ownership, and review lanes (`architect`, `critic`) stay read-only when resumed.
|
|
208
|
+
- Resumption never weakens gates: a resumed `architect` review or `executor` QA lane must still evaluate the current frozen change set on its own evidence, not rubber-stamp its earlier verdict.
|
|
209
|
+
|
|
210
|
+
If an Ultragoal request has no approved plan or consensus artifact **and** the scope genuinely needs one, run `ralplan` first and preserve its PRD, test spec, role roster, and verification guidance in the Ultragoal ledger. Skip `ralplan` for small scope: work that fits a single reviewable PR and is tied to a single domain/subsystem can proceed directly from the brief — record that judgment in the ledger instead of running a planning round. Reach for `ralplan` when the scope spans multiple domains/subsystems, needs cross-cutting sequencing, or would not fit a single PR.
|
|
203
211
|
|
|
204
212
|
The Ultragoal leader owns `.gjc/_session-{sessionid}/ultragoal/goals.json` and `.gjc/_session-{sessionid}/ultragoal/ledger.jsonl`. Role agents return implementation/review evidence; they do not checkpoint Ultragoal or mutate goal state.
|
|
205
213
|
|
|
@@ -207,8 +215,8 @@ The Ultragoal leader owns `.gjc/_session-{sessionid}/ultragoal/goals.json` and `
|
|
|
207
215
|
|
|
208
216
|
Native subagent parallelism is a contract for bounded `executor` delegation, not a runtime scheduler and not a Team-mode rule:
|
|
209
217
|
|
|
210
|
-
- **
|
|
211
|
-
- **
|
|
218
|
+
- **Use native `executor` parallelism only** when a story's expected diffs fall in genuinely different sub-domains/modules/systems, each boundable by a per-slice coordination contract.
|
|
219
|
+
- **Default to direct leader edits** otherwise; sequence any work with real dependencies, shared-file overlap, or a single-domain footprint, and never parallelize work that lacks a safe contract.
|
|
212
220
|
- Worker agents **MUST NOT mutate `.gjc/_session-{sessionid}/ultragoal`**, call goal tools, make checkpoint decisions, own integration, or own final verification. The Ultragoal leader keeps those responsibilities.
|
|
213
221
|
|
|
214
222
|
Before workers start, each per-slice coordination contract MUST name the target files/surfaces, independence assumptions, allowed coordination channel, conflict-escalation rule, expected evidence, and terminal status. Conflict or assignment changes remain leader-owned and must be auditable through durable ledger evidence.
|
|
@@ -373,6 +381,8 @@ The critic must verify that the `human_blocked` classification is genuine, inclu
|
|
|
373
381
|
|
|
374
382
|
At each terminus, the leader gives the read-only `critic` role agent `brief.md`, `goals.json`, `ledger.jsonl`, and the cumulative change set. For completion, invoke it before assembling the final-aggregate gate JSON. For pause, invoke it after the `human_blocked` classification and before `goal({"op":"pause"})`. The terminal critic must not spawn nested `ralplan`, `team`, `deep-interview`, or `ultragoal` workflows. This creates no interactive surface: `ask` remains blocked while an Ultragoal run is active.
|
|
375
383
|
|
|
384
|
+
On repeat terminus attempts within the same run (after an `ITERATE`/`REJECT` reopen cycle or a superseded pause classification), **resume the prior terminal-critic subagent when resumable** instead of freshly spawning one: the critic already holds `brief.md`, `goals.json`, the ledger history, and its own prior findings, so re-invocation only needs the delta (new ledger events, the updated cumulative change set, and evidence addressing the prior blockers). Resume via existing `subagent` resume/steer controls; on `context_unavailable`, `not_found`, `no_runner`, or `resume_failed` — or after a process restart — fall back to a fresh `critic` spawn with the full context bundle. A resumed terminal critic remains read-only, keeps the same containment rules, and must issue a fresh verdict against the current state — a prior `ITERATE` is never carried forward as pre-judged, and each verdict is still recorded through `gjc ultragoal record-critic-verdict`.
|
|
385
|
+
|
|
376
386
|
### Non-OKAY loop and ceiling
|
|
377
387
|
|
|
378
388
|
For completion-side `ITERATE` or `REJECT`, the leader MUST first record the terminal verdict so the run-level counter observes it: `gjc ultragoal record-critic-verdict --terminus completion --verdict <ITERATE|REJECT> --evidence "<critic findings>"`; then record the findings with `gjc ultragoal record-review-blockers` and reopen the run. The dedicated counter ceiling is 5, independently of the give-up nudge budget, and is **RUN-LEVEL**: it counts every non-OKAY terminal-critic verdict across the whole run and all reopen cycles. On reaching that ceiling, both pause and final completion are blocked until a human or leader records `gjc ultragoal record-critic-gate-override --evidence "<authorization evidence>"`. There is no automatic pause override.
|
package/src/eval/py/executor.ts
CHANGED
|
@@ -514,8 +514,11 @@ async function executeWithKernel(
|
|
|
514
514
|
});
|
|
515
515
|
|
|
516
516
|
if (result.cancelled) {
|
|
517
|
+
// Prefer the caller-configured timeout for the user-facing annotation.
|
|
518
|
+
// Remaining wall-clock budget can shrink after async setup (Settings.init,
|
|
519
|
+
// kernel start) and would otherwise flake Math.round() second formatting.
|
|
517
520
|
const annotation = result.timedOut
|
|
518
|
-
? formatKernelTimeoutAnnotation(executionTimeoutMs, result.kernelKilled ?? false)
|
|
521
|
+
? formatKernelTimeoutAnnotation(options?.timeoutMs ?? executionTimeoutMs, result.kernelKilled ?? false)
|
|
519
522
|
: undefined;
|
|
520
523
|
let crashNotice: string | null = null;
|
|
521
524
|
if (result.kernelKilled) {
|
|
@@ -569,7 +572,9 @@ async function executeWithKernel(
|
|
|
569
572
|
cancelled: true,
|
|
570
573
|
displayOutputs,
|
|
571
574
|
stdinRequested: false,
|
|
572
|
-
...(await sink.dump(
|
|
575
|
+
...(await sink.dump(
|
|
576
|
+
timedOut ? formatTimeoutAnnotation(options?.timeoutMs ?? executionTimeoutMs) : undefined,
|
|
577
|
+
)),
|
|
573
578
|
};
|
|
574
579
|
}
|
|
575
580
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -392,7 +392,15 @@ export interface ExtensionContext {
|
|
|
392
392
|
|
|
393
393
|
getJobs(): unknown;
|
|
394
394
|
/** Typed skill and mode controls exposed to the SDK host. */
|
|
395
|
-
invokeSkill?(
|
|
395
|
+
invokeSkill?(
|
|
396
|
+
name: string,
|
|
397
|
+
args?: string,
|
|
398
|
+
options?: {
|
|
399
|
+
onPreflightAccepted?: () => void;
|
|
400
|
+
onPreflightAcceptCommit?: () => void | Promise<void>;
|
|
401
|
+
onSkillPrepared?: (meta: { name: string; path: string; lineCount?: number; cleanedArgs?: string }) => void;
|
|
402
|
+
},
|
|
403
|
+
): Promise<unknown>;
|
|
396
404
|
setPlanMode?(on: boolean): unknown;
|
|
397
405
|
operateGoal?(op: "create" | "get" | "resume" | "pause" | "complete" | "drop", objective?: string): Promise<unknown>;
|
|
398
406
|
|
|
@@ -1135,7 +1143,11 @@ export interface ExtensionAPI {
|
|
|
1135
1143
|
/** Send a user message to the agent, or queue it when deliverAs is set. */
|
|
1136
1144
|
sendUserMessage(
|
|
1137
1145
|
content: string | (TextContent | ImageContent)[],
|
|
1138
|
-
options?: {
|
|
1146
|
+
options?: {
|
|
1147
|
+
deliverAs?: "steer" | "followUp";
|
|
1148
|
+
onPreflightAccepted?: () => void;
|
|
1149
|
+
onPreflightAcceptCommit?: () => void | Promise<void>;
|
|
1150
|
+
},
|
|
1139
1151
|
): Promise<void>;
|
|
1140
1152
|
|
|
1141
1153
|
/** Append a custom entry to the session for state persistence (not sent to LLM). */
|
|
@@ -1344,7 +1356,11 @@ export type SendMessageHandler = <T = unknown>(
|
|
|
1344
1356
|
|
|
1345
1357
|
export type SendUserMessageHandler = (
|
|
1346
1358
|
content: string | (TextContent | ImageContent)[],
|
|
1347
|
-
options?: {
|
|
1359
|
+
options?: {
|
|
1360
|
+
deliverAs?: "steer" | "followUp";
|
|
1361
|
+
onPreflightAccepted?: () => void;
|
|
1362
|
+
onPreflightAcceptCommit?: () => void | Promise<void>;
|
|
1363
|
+
},
|
|
1348
1364
|
) => void | Promise<void>;
|
|
1349
1365
|
|
|
1350
1366
|
export type AppendEntryHandler = <T = unknown>(customType: string, data?: T) => void;
|
|
@@ -1469,7 +1485,15 @@ export interface ExtensionContextActions {
|
|
|
1469
1485
|
| undefined,
|
|
1470
1486
|
) => void;
|
|
1471
1487
|
sdkControl?: (operation: string, input: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
1472
|
-
invokeSkill?: (
|
|
1488
|
+
invokeSkill?: (
|
|
1489
|
+
name: string,
|
|
1490
|
+
args?: string,
|
|
1491
|
+
options?: {
|
|
1492
|
+
onPreflightAccepted?: () => void;
|
|
1493
|
+
onPreflightAcceptCommit?: () => void | Promise<void>;
|
|
1494
|
+
onSkillPrepared?: (meta: { name: string; path: string; lineCount?: number; cleanedArgs?: string }) => void;
|
|
1495
|
+
},
|
|
1496
|
+
) => Promise<unknown>;
|
|
1473
1497
|
setPlanMode?: (on: boolean) => unknown;
|
|
1474
1498
|
operateGoal?: (
|
|
1475
1499
|
op: "create" | "get" | "resume" | "pause" | "complete" | "drop",
|
|
@@ -38,6 +38,9 @@ export interface SessionBeforeSwitchEvent {
|
|
|
38
38
|
targetSessionFile?: string;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/** Origin used when an interactive session selector resumes a session. */
|
|
42
|
+
export const INTERACTIVE_SELECTOR_RESUME_ORIGIN = "interactive_selector_resume";
|
|
43
|
+
|
|
41
44
|
/** Fired after switching to another session */
|
|
42
45
|
export interface SessionSwitchEvent {
|
|
43
46
|
type: "session_switch";
|
|
@@ -45,6 +48,8 @@ export interface SessionSwitchEvent {
|
|
|
45
48
|
reason: "new" | "resume" | "fork";
|
|
46
49
|
/** Session file we came from */
|
|
47
50
|
previousSessionFile: string | undefined;
|
|
51
|
+
/** Optional provenance for this session transition. */
|
|
52
|
+
transition?: { origin: string };
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
/** Fired before branching a session (can be cancelled) */
|
|
@@ -25,6 +25,14 @@ import {
|
|
|
25
25
|
* (no filesystem, no state-writer, no CLI runtime) so every writer can apply it.
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
+
// =============================================================================
|
|
29
|
+
// Weights (mirrors the Ouroboros deterministic_floor coefficients)
|
|
30
|
+
// =============================================================================
|
|
31
|
+
|
|
32
|
+
const DISPUTED_FACT_WEIGHT = 0.1;
|
|
33
|
+
const UNSCORED_COMPONENT_WEIGHT = 0.05;
|
|
34
|
+
const AUTO_ANSWER_DILUTION_WEIGHT = 0.05;
|
|
35
|
+
|
|
28
36
|
const CORE_CLARITY_DIMENSIONS = ["goal", "constraints", "criteria"] as const;
|
|
29
37
|
|
|
30
38
|
export interface AmbiguityFloorBreakdown {
|
|
@@ -51,57 +59,64 @@ function asArray(value: unknown): unknown[] {
|
|
|
51
59
|
return Array.isArray(value) ? value : [];
|
|
52
60
|
}
|
|
53
61
|
|
|
54
|
-
|
|
62
|
+
function round2(value: number): number {
|
|
63
|
+
return Math.round(value * 100) / 100;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** A disputed fact contributes pressure until it is resolved via `superseded_by` or re-confirmation. */
|
|
55
67
|
function isUnresolvedDisputedFact(value: unknown): boolean {
|
|
56
68
|
if (!isPlainObject(value)) return false;
|
|
57
|
-
const fact = value as Partial<DeepInterviewEstablishedFact
|
|
58
|
-
if (fact.disputed !== true
|
|
69
|
+
const fact = value as Partial<DeepInterviewEstablishedFact>;
|
|
70
|
+
if (fact.disputed !== true) return false;
|
|
59
71
|
return typeof fact.superseded_by !== "string" || fact.superseded_by.trim() === "";
|
|
60
72
|
}
|
|
61
73
|
|
|
62
74
|
/**
|
|
63
75
|
* An active component is unscored while any core clarity dimension lacks a finite
|
|
64
|
-
* numeric score. Deferred
|
|
76
|
+
* numeric score. Deferred components are excluded (they are outside ambiguity math),
|
|
77
|
+
* and the gate only arms once the topology was explicitly confirmed in Round 0.
|
|
65
78
|
*/
|
|
66
79
|
function countUnscoredActiveComponents(topology: unknown): number {
|
|
67
80
|
if (!isPlainObject(topology) || topology.status !== "confirmed") return 0;
|
|
68
81
|
let unscored = 0;
|
|
69
82
|
for (const component of asArray(topology.components)) {
|
|
70
|
-
if (!isPlainObject(component) || component.status === "deferred"
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (
|
|
77
|
-
CORE_CLARITY_DIMENSIONS.some(dimension => {
|
|
78
|
-
const score = scores[dimension];
|
|
79
|
-
return typeof score !== "number" || !Number.isFinite(score);
|
|
80
|
-
})
|
|
81
|
-
) {
|
|
82
|
-
unscored += 1;
|
|
83
|
-
}
|
|
83
|
+
if (!isPlainObject(component) || component.status === "deferred") continue;
|
|
84
|
+
const clarity = isPlainObject(component.clarity_scores) ? component.clarity_scores : {};
|
|
85
|
+
const incomplete = CORE_CLARITY_DIMENSIONS.some(dimension => {
|
|
86
|
+
const score = clarity[dimension];
|
|
87
|
+
return typeof score !== "number" || !Number.isFinite(score);
|
|
88
|
+
});
|
|
89
|
+
if (incomplete) unscored += 1;
|
|
84
90
|
}
|
|
85
91
|
return unscored;
|
|
86
92
|
}
|
|
87
93
|
|
|
88
|
-
|
|
94
|
+
function autoAnswerRatio(inner: Record<string, unknown>): number {
|
|
95
|
+
const autoAnswered = asArray(inner.auto_answered_rounds).length;
|
|
96
|
+
if (autoAnswered === 0) return 0;
|
|
97
|
+
const scored = asArray(inner.rounds).filter(round => isPlainObject(round) && round.lifecycle === "scored").length;
|
|
98
|
+
return Math.min(1, autoAnswered / Math.max(scored, 1));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// =============================================================================
|
|
102
|
+
// Floor computation + clamping
|
|
103
|
+
// =============================================================================
|
|
104
|
+
|
|
105
|
+
/** Compute the deterministic floor from the inner deep-interview `state` object. */
|
|
89
106
|
export function computeAmbiguityFloor(inner: unknown): AmbiguityFloorBreakdown {
|
|
90
107
|
const state = isPlainObject(inner) ? inner : {};
|
|
91
108
|
const disputedFactCount = asArray(state.established_facts).filter(isUnresolvedDisputedFact).length;
|
|
92
109
|
const unscoredActiveComponentCount = countUnscoredActiveComponents(state.topology);
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
disputedFactCount * 1_000 + unscoredActiveComponentCount * 500 + Math.floor(ratioUnits / 20),
|
|
99
|
-
);
|
|
110
|
+
const ratio = autoAnswerRatio(state);
|
|
111
|
+
const floor =
|
|
112
|
+
DISPUTED_FACT_WEIGHT * disputedFactCount +
|
|
113
|
+
UNSCORED_COMPONENT_WEIGHT * unscoredActiveComponentCount +
|
|
114
|
+
AUTO_ANSWER_DILUTION_WEIGHT * ratio;
|
|
100
115
|
return {
|
|
101
|
-
floor:
|
|
116
|
+
floor: round2(Math.min(1, Math.max(0, floor))),
|
|
102
117
|
disputed_fact_count: disputedFactCount,
|
|
103
118
|
unscored_active_component_count: unscoredActiveComponentCount,
|
|
104
|
-
auto_answer_ratio:
|
|
119
|
+
auto_answer_ratio: round2(ratio),
|
|
105
120
|
};
|
|
106
121
|
}
|
|
107
122
|
|
|
@@ -113,8 +128,10 @@ export function clampReportedAmbiguity(reported: number, floor: number): Ambigui
|
|
|
113
128
|
}
|
|
114
129
|
|
|
115
130
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
131
|
+
* Mark every established fact recorded in `retractedRound` as disputed. This is the
|
|
132
|
+
* mechanical contradiction signal for answer retraction: when the user replaces an
|
|
133
|
+
* already-scored answer (A -> B pivot), the facts that answer established can no
|
|
134
|
+
* longer be trusted, so they raise the floor until re-confirmed or superseded.
|
|
118
135
|
* Returns new arrays/objects; never mutates the input.
|
|
119
136
|
*/
|
|
120
137
|
export function disputeFactsFromRetractedRound(
|
|
@@ -187,55 +204,3 @@ export function applyAmbiguityFloorToEnvelope(value: unknown): AppliedAmbiguityF
|
|
|
187
204
|
inner.ambiguity_floor = breakdown;
|
|
188
205
|
return { envelope: { ...envelope, state: inner }, breakdown, clamped };
|
|
189
206
|
}
|
|
190
|
-
export type DeepInterviewAmbiguityMilestone = "initial" | "progress" | "refined" | "ready";
|
|
191
|
-
|
|
192
|
-
export function deriveAmbiguityMilestone(
|
|
193
|
-
effectiveUnits: number,
|
|
194
|
-
thresholdUnits: number,
|
|
195
|
-
): DeepInterviewAmbiguityMilestone {
|
|
196
|
-
if (!Number.isSafeInteger(effectiveUnits) || effectiveUnits < 0 || effectiveUnits > 10_000) {
|
|
197
|
-
throw new RangeError("effective ambiguity units must be an integer from 0 to 10000");
|
|
198
|
-
}
|
|
199
|
-
if (!Number.isSafeInteger(thresholdUnits) || thresholdUnits < 1 || thresholdUnits > 10_000) {
|
|
200
|
-
throw new RangeError("threshold units must be an integer from 1 to 10000");
|
|
201
|
-
}
|
|
202
|
-
if (effectiveUnits <= thresholdUnits) return "ready";
|
|
203
|
-
if (effectiveUnits > 6_000) return "initial";
|
|
204
|
-
if (effectiveUnits > 3_000) return "progress";
|
|
205
|
-
return "refined";
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
export function scoreToUnits(score: number): number {
|
|
209
|
-
if (!Number.isFinite(score) || score < 0 || score > 1) throw new RangeError("score must be finite in [0, 1]");
|
|
210
|
-
/**
|
|
211
|
-
* Scores carry at most four decimal places, but `score * 10_000` misses the
|
|
212
|
-
* integer grid for ordinary values: `0.69 * 10_000` is `6900.000000000001` and
|
|
213
|
-
* `0.07 * 10_000` is `700.0000000000001`. Ambiguity is fed straight back in as
|
|
214
|
-
* `units / 10_000`, so testing the float product rejected most real scores.
|
|
215
|
-
*
|
|
216
|
-
* Decide from the shortest round-trip decimal instead. That is exact: every
|
|
217
|
-
* genuine 1e-4 value has at most four fraction digits, while off-grid
|
|
218
|
-
* precision keeps its digits (`0.00005`, `0.05000000000000001`) and is
|
|
219
|
-
* rejected. An epsilon on the product cannot separate those two cases -- both
|
|
220
|
-
* residuals are ~1e-13. The `[0, 1]` guard plus the four-digit cap already
|
|
221
|
-
* bound the result to an integer in `[0, 10_000]`.
|
|
222
|
-
*/
|
|
223
|
-
const decimal = /^(\d+)(?:\.(\d{1,4}))?$/.exec(String(score));
|
|
224
|
-
if (!decimal) throw new RangeError("score must be expressed in integral 1e-4 units");
|
|
225
|
-
const units = Number(decimal[1]) * 10_000 + Number((decimal[2] ?? "").padEnd(4, "0"));
|
|
226
|
-
return units;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export function weightedAmbiguityUnits(
|
|
230
|
-
scores: { goal: number; constraints: number; criteria: number; context?: number },
|
|
231
|
-
type: "greenfield" | "brownfield",
|
|
232
|
-
): number {
|
|
233
|
-
const goal = scoreToUnits(scores.goal);
|
|
234
|
-
const constraints = scoreToUnits(scores.constraints);
|
|
235
|
-
const criteria = scoreToUnits(scores.criteria);
|
|
236
|
-
const numerator =
|
|
237
|
-
type === "brownfield"
|
|
238
|
-
? 35 * goal + 25 * constraints + 25 * criteria + 15 * scoreToUnits(scores.context ?? Number.NaN)
|
|
239
|
-
: 40 * goal + 30 * constraints + 30 * criteria;
|
|
240
|
-
return 10_000 - Math.floor(numerator / 100 + 0.5);
|
|
241
|
-
}
|