@gajae-code/coding-agent 0.11.7 → 0.11.9

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.
Files changed (213) hide show
  1. package/CHANGELOG.md +73 -1
  2. package/dist/types/cli/args.d.ts +0 -2
  3. package/dist/types/cli.d.ts +1 -0
  4. package/dist/types/config/keybindings.d.ts +33 -3
  5. package/dist/types/config/model-profile-activation.d.ts +4 -3
  6. package/dist/types/config/model-profile-contract.d.ts +38 -0
  7. package/dist/types/config/model-profiles.d.ts +2 -3
  8. package/dist/types/config/settings-schema.d.ts +15 -2
  9. package/dist/types/edit/modes/patch.d.ts +13 -0
  10. package/dist/types/edit/path-mutation-lock.d.ts +15 -0
  11. package/dist/types/eval/py/executor.d.ts +1 -1
  12. package/dist/types/eval/py/tool-bridge.d.ts +1 -2
  13. package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +12 -5
  14. package/dist/types/gjc-runtime/deep-interview-draft.d.ts +8 -0
  15. package/dist/types/gjc-runtime/deep-interview-payload.d.ts +41 -0
  16. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +32 -17
  17. package/dist/types/gjc-runtime/deep-interview-repair.d.ts +17 -0
  18. package/dist/types/gjc-runtime/deep-interview-state.d.ts +135 -4
  19. package/dist/types/gjc-runtime/repository-binding.d.ts +63 -0
  20. package/dist/types/gjc-runtime/state-writer.d.ts +44 -0
  21. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +1 -0
  22. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +24 -0
  23. package/dist/types/gjc-runtime/workflow-manifest.d.ts +5 -3
  24. package/dist/types/main.d.ts +8 -0
  25. package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
  26. package/dist/types/modes/components/tool-status-header.d.ts +3 -2
  27. package/dist/types/modes/components/welcome.d.ts +4 -0
  28. package/dist/types/modes/controllers/command-controller.d.ts +2 -0
  29. package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
  30. package/dist/types/modes/interactive-mode.d.ts +5 -2
  31. package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
  32. package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
  33. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
  34. package/dist/types/modes/types.d.ts +3 -0
  35. package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
  36. package/dist/types/runtime-mcp/manager.d.ts +1 -1
  37. package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
  38. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
  39. package/dist/types/runtime-mcp/redaction.d.ts +2 -0
  40. package/dist/types/sdk/broker/broker.d.ts +2 -0
  41. package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
  42. package/dist/types/sdk/bus/chat-daemon-control.d.ts +7 -1
  43. package/dist/types/sdk/bus/config-commands.d.ts +2 -2
  44. package/dist/types/sdk/bus/config.d.ts +2 -0
  45. package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
  46. package/dist/types/sdk/bus/index.d.ts +25 -0
  47. package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
  48. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +105 -0
  49. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +15 -3
  50. package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
  51. package/dist/types/sdk/bus/telegram-daemon.d.ts +128 -1
  52. package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
  53. package/dist/types/sdk/host/control/operations.d.ts +1 -1
  54. package/dist/types/sdk/host/host.d.ts +1 -1
  55. package/dist/types/sdk/host/query/handlers.d.ts +9 -0
  56. package/dist/types/sdk/index.d.ts +2 -0
  57. package/dist/types/sdk/prompt-status.d.ts +88 -0
  58. package/dist/types/sdk/session-directory.d.ts +1 -1
  59. package/dist/types/sdk/startup-capability.d.ts +3 -0
  60. package/dist/types/session/agent-session.d.ts +2 -0
  61. package/dist/types/session/internal/managed-session-scope.d.ts +22 -3
  62. package/dist/types/session/internal/managed-session-storage.d.ts +24 -4
  63. package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
  64. package/dist/types/session/session-manager.d.ts +5 -1
  65. package/dist/types/session/session-storage.d.ts +31 -0
  66. package/dist/types/skill-state/active-state.d.ts +4 -0
  67. package/dist/types/task/index.d.ts +3 -1
  68. package/dist/types/task/receipt.d.ts +2 -0
  69. package/dist/types/task/types.d.ts +94 -0
  70. package/dist/types/tools/ask.d.ts +454 -174
  71. package/dist/types/tools/image-gen.d.ts +47 -1
  72. package/dist/types/tools/index.d.ts +4 -0
  73. package/dist/types/utils/prompt-suggestion.d.ts +33 -0
  74. package/package.json +8 -8
  75. package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
  76. package/scripts/dogfood-repository-binding.ts +209 -0
  77. package/scripts/generate-sdk-operation-inventory.ts +2 -0
  78. package/scripts/run-test-manifest.ts +4 -4
  79. package/src/cli/args.ts +2 -92
  80. package/src/cli/mcp-cli.ts +4 -21
  81. package/src/cli/notify-cli.ts +5 -38
  82. package/src/cli.ts +50 -0
  83. package/src/commands/deep-interview.ts +9 -4
  84. package/src/commands/sdk.ts +2 -2
  85. package/src/commit/agentic/index.ts +3 -3
  86. package/src/commit/map-reduce/index.ts +2 -2
  87. package/src/config/keybindings.ts +157 -24
  88. package/src/config/model-profile-activation.ts +7 -18
  89. package/src/config/model-profile-contract.ts +161 -0
  90. package/src/config/model-profiles.ts +2 -3
  91. package/src/config/model-registry.ts +11 -1
  92. package/src/config/settings-schema.ts +17 -2
  93. package/src/coordinator-mcp/model-preset.ts +21 -74
  94. package/src/defaults/gjc/skills/deep-interview/SKILL.md +80 -165
  95. package/src/defaults/gjc/skills/ralplan/SKILL.md +3 -1
  96. package/src/edit/index.ts +3 -6
  97. package/src/edit/modes/patch.ts +38 -1
  98. package/src/edit/modes/replace.ts +42 -0
  99. package/src/edit/path-mutation-lock.ts +112 -0
  100. package/src/eval/py/executor.ts +30 -11
  101. package/src/eval/py/prelude.py +2 -2
  102. package/src/eval/py/tool-bridge.ts +34 -18
  103. package/src/extensibility/gjc-plugins/runtime-adapters.ts +2 -1
  104. package/src/gjc-runtime/deep-interview-ambiguity.ts +68 -47
  105. package/src/gjc-runtime/deep-interview-draft.ts +842 -0
  106. package/src/gjc-runtime/deep-interview-payload.ts +262 -0
  107. package/src/gjc-runtime/deep-interview-recorder.ts +294 -226
  108. package/src/gjc-runtime/deep-interview-repair.ts +1674 -0
  109. package/src/gjc-runtime/deep-interview-runtime.ts +264 -103
  110. package/src/gjc-runtime/deep-interview-state.ts +916 -4
  111. package/src/gjc-runtime/launch-tmux.ts +1 -1
  112. package/src/gjc-runtime/managed-owner-supervisor.ts +83 -4
  113. package/src/gjc-runtime/ralplan-runtime.ts +78 -4
  114. package/src/gjc-runtime/repository-binding.ts +267 -0
  115. package/src/gjc-runtime/session-state-sidecar.ts +25 -9
  116. package/src/gjc-runtime/state-writer.ts +166 -2
  117. package/src/gjc-runtime/tmux-owner-isolation.ts +2 -1
  118. package/src/gjc-runtime/tmux-sessions.ts +45 -6
  119. package/src/gjc-runtime/ultragoal-guard.ts +5 -5
  120. package/src/gjc-runtime/ultragoal-runtime.ts +181 -19
  121. package/src/gjc-runtime/workflow-manifest.generated.json +386 -1
  122. package/src/gjc-runtime/workflow-manifest.ts +191 -5
  123. package/src/internal-urls/docs-index.generated.ts +8 -6
  124. package/src/internal-urls/local-protocol.ts +33 -16
  125. package/src/internal-urls/mcp-protocol.ts +73 -11
  126. package/src/main.ts +25 -10
  127. package/src/modes/DESIGN.md +5 -0
  128. package/src/modes/acp/acp-agent.ts +50 -16
  129. package/src/modes/components/model-selector.ts +42 -8
  130. package/src/modes/components/plan-preview-overlay.ts +1 -1
  131. package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
  132. package/src/modes/components/tool-status-header.ts +20 -4
  133. package/src/modes/components/welcome.ts +32 -20
  134. package/src/modes/controllers/command-controller.ts +97 -54
  135. package/src/modes/controllers/event-controller.ts +10 -0
  136. package/src/modes/controllers/input-controller.ts +7 -2
  137. package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
  138. package/src/modes/controllers/selector-controller.ts +46 -54
  139. package/src/modes/interactive-mode.ts +70 -36
  140. package/src/modes/prompt-action-autocomplete.ts +32 -13
  141. package/src/modes/prompt-suggestion-controller.ts +96 -0
  142. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
  143. package/src/modes/types.ts +3 -0
  144. package/src/modes/utils/hotkeys-markdown.ts +64 -36
  145. package/src/modes/utils/ui-helpers.ts +5 -3
  146. package/src/prompts/agents/architect.md +0 -1
  147. package/src/prompts/agents/critic.md +0 -1
  148. package/src/prompts/system/prompt-suggestion-system.md +32 -0
  149. package/src/prompts/system/system-prompt.md +7 -4
  150. package/src/prompts/tools/cron.md +3 -1
  151. package/src/runtime-mcp/json-rpc.ts +7 -4
  152. package/src/runtime-mcp/manager.ts +51 -53
  153. package/src/runtime-mcp/oauth-flow.ts +0 -72
  154. package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
  155. package/src/runtime-mcp/redaction.ts +30 -0
  156. package/src/runtime-mcp/transports/http.ts +13 -6
  157. package/src/sdk/broker/broker.ts +34 -20
  158. package/src/sdk/broker/lifecycle.ts +185 -36
  159. package/src/sdk/broker/session-index.ts +11 -1
  160. package/src/sdk/bus/chat-daemon-control.ts +20 -5
  161. package/src/sdk/bus/config-commands.ts +2 -2
  162. package/src/sdk/bus/config.ts +9 -1
  163. package/src/sdk/bus/conversation-store.ts +40 -6
  164. package/src/sdk/bus/index.ts +240 -65
  165. package/src/sdk/bus/lifecycle-commands.ts +47 -3
  166. package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
  167. package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
  168. package/src/sdk/bus/notification-orchestration.ts +12 -0
  169. package/src/sdk/bus/notification-service.ts +27 -2
  170. package/src/sdk/bus/prompt-reconciliation.ts +239 -0
  171. package/src/sdk/bus/recent-activity.ts +90 -26
  172. package/src/sdk/bus/telegram-daemon-contract.ts +15 -3
  173. package/src/sdk/bus/telegram-daemon-control.ts +145 -33
  174. package/src/sdk/bus/telegram-daemon.ts +853 -112
  175. package/src/sdk/bus/telegram-reference.ts +20 -2
  176. package/src/sdk/cli/session-cli.ts +15 -2
  177. package/src/sdk/host/control/dispatch.ts +1 -1
  178. package/src/sdk/host/control/operations.ts +1 -1
  179. package/src/sdk/host/host.ts +2 -6
  180. package/src/sdk/host/query/handlers.ts +70 -5
  181. package/src/sdk/index.ts +12 -0
  182. package/src/sdk/prompt-status.ts +97 -0
  183. package/src/sdk/protocol/operation-inventory.generated.json +47 -0
  184. package/src/sdk/protocol/operation-registry.ts +18 -4
  185. package/src/sdk/session-directory.ts +5 -1
  186. package/src/sdk/session.ts +2 -0
  187. package/src/sdk/startup-capability.ts +9 -1
  188. package/src/session/agent-session.ts +138 -5
  189. package/src/session/blob-store.ts +7 -2
  190. package/src/session/internal/managed-session-scope.ts +998 -133
  191. package/src/session/internal/managed-session-storage.ts +268 -203
  192. package/src/session/internal/native-publish-outcome.ts +266 -0
  193. package/src/session/session-manager.ts +406 -360
  194. package/src/session/session-storage.ts +151 -11
  195. package/src/skill-state/active-state.ts +10 -0
  196. package/src/task/executor.ts +13 -5
  197. package/src/task/gjc-command.ts +2 -2
  198. package/src/task/index.ts +121 -15
  199. package/src/task/receipt.ts +3 -0
  200. package/src/task/types.ts +34 -3
  201. package/src/tools/ask.ts +199 -88
  202. package/src/tools/ast-edit.ts +2 -2
  203. package/src/tools/bash-pty-selection.ts +2 -2
  204. package/src/tools/browser/screenshot-format.ts +6 -1
  205. package/src/tools/cron.ts +1 -1
  206. package/src/tools/image-gen.ts +178 -6
  207. package/src/tools/index.ts +4 -0
  208. package/src/tools/render-utils.ts +7 -1
  209. package/src/utils/edit-mode.ts +2 -2
  210. package/src/utils/prompt-suggestion.ts +265 -0
  211. package/src/utils/sixel.ts +3 -3
  212. package/src/web/insane/url-guard.ts +3 -1
  213. package/src/web/scrapers/docs-rs.ts +30 -5
@@ -1,44 +1,10 @@
1
1
  import * as path from "node:path";
2
2
  import { getAgentDir } from "@gajae-code/utils";
3
3
  import { YAML } from "bun";
4
+ import { UnknownModelProfileError, validateModelProfileName } from "../config/model-profile-contract";
5
+ import { mergeModelProfiles } from "../config/model-profiles";
4
6
  import { ModelsConfigSchema } from "../config/models-config-schema";
5
7
 
6
- /**
7
- * The coordinator runs in the shipped MCP process and must stay outside the
8
- * session host import graph. Keep this identity catalog in the coordinator
9
- * boundary; profile activation and model registry code belong to that host.
10
- */
11
- const BUILTIN_MODEL_PROFILE_NAMES = [
12
- "codex-eco",
13
- "codex-medium",
14
- "codex-pro",
15
- "opencodego",
16
- "claude-opus",
17
- "claude-fable",
18
- "glm-eco",
19
- "glm-medium",
20
- "glm-pro",
21
- "kimi-coding-plan-eco",
22
- "kimi-coding-plan-medium",
23
- "kimi-coding-plan-pro",
24
- "mimo-eco",
25
- "mimo-medium",
26
- "mimo-pro",
27
- "grok-eco",
28
- "grok-medium",
29
- "grok-pro",
30
- "grok-build-pro",
31
- "cursor-eco",
32
- "cursor-medium",
33
- "cursor-pro",
34
- "minimax-eco",
35
- "minimax-medium",
36
- "minimax-pro",
37
- "opus-codex",
38
- "codex-opencodego",
39
- "fable-opus-codex",
40
- ] as const;
41
-
42
8
  export interface CoordinatorModelProfile {
43
9
  name: string;
44
10
  }
@@ -53,8 +19,6 @@ export type CoordinatorModelProfileLoader = () =>
53
19
  | Promise<Map<string, CoordinatorModelProfile>>;
54
20
 
55
21
  const MAX_ECHOED_MPRESET_LENGTH = 128;
56
- const LEGACY_MODEL_PROFILE_ALIASES: ReadonlyMap<string, string> = new Map([["codex-standard", "codex-medium"]]);
57
-
58
22
  /**
59
23
  * Thrown by the default loader when `models.yml` exists but is invalid or
60
24
  * unreadable. This lets the resolver fail closed with a distinct, stable reason
@@ -69,35 +33,25 @@ export class CoordinatorModelProfileRegistryError extends Error {
69
33
  }
70
34
  }
71
35
 
72
- function builtInCoordinatorModelProfiles(): Map<string, CoordinatorModelProfile> {
73
- return new Map(BUILTIN_MODEL_PROFILE_NAMES.map(name => [name, { name }]));
36
+ function coordinatorModelProfiles(
37
+ profiles?: Parameters<typeof mergeModelProfiles>[0],
38
+ ): Map<string, CoordinatorModelProfile> {
39
+ return new Map([...mergeModelProfiles(profiles).keys()].map(name => [name, { name }]));
74
40
  }
75
41
 
76
42
  export const loadCoordinatorModelProfiles: CoordinatorModelProfileLoader = async () => {
77
43
  const modelsFile = Bun.file(path.join(getAgentDir(), "models.yml"));
78
- if (!(await modelsFile.exists())) return builtInCoordinatorModelProfiles();
44
+ if (!(await modelsFile.exists())) return coordinatorModelProfiles();
79
45
  try {
80
46
  const parsed = YAML.parse(await modelsFile.text());
81
47
  const config = ModelsConfigSchema.safeParse(parsed);
82
48
  if (!config.success) throw config.error;
83
- const profiles = builtInCoordinatorModelProfiles();
84
- for (const name of Object.keys(config.data.profiles ?? {})) profiles.set(name, { name });
85
- return profiles;
49
+ return coordinatorModelProfiles(config.data.profiles);
86
50
  } catch (error) {
87
51
  throw new CoordinatorModelProfileRegistryError(error);
88
52
  }
89
53
  };
90
54
 
91
- function sortedProfileNames(profiles: ReadonlyMap<string, CoordinatorModelProfile>): string[] {
92
- return [...profiles.keys()].sort((left, right) => left.localeCompare(right));
93
- }
94
-
95
- function resolveCoordinatorModelProfileName(profileName: string, profiles: ReadonlyMap<string, unknown>): string {
96
- if (profiles.has(profileName)) return profileName;
97
- const replacement = LEGACY_MODEL_PROFILE_ALIASES.get(profileName);
98
- return replacement && profiles.has(replacement) ? replacement : profileName;
99
- }
100
-
101
55
  export type CoordinatorMpresetResolution =
102
56
  | { ok: true; mpreset: string | null }
103
57
  | { ok: false; reason: "unknown_model_profile"; mpreset: string; available_profiles: string[] }
@@ -120,7 +74,7 @@ export async function resolveCoordinatorMpreset(
120
74
  loadProfiles: CoordinatorModelProfileLoader,
121
75
  ): Promise<CoordinatorMpresetResolution> {
122
76
  if (raw === undefined || raw === null) return { ok: true, mpreset: null };
123
- const requested = typeof raw === "string" ? raw.trim() : "";
77
+ const requested = typeof raw === "string" ? raw : "";
124
78
  const echoed = requested.slice(0, MAX_ECHOED_MPRESET_LENGTH);
125
79
  let profiles: Map<string, CoordinatorModelProfile>;
126
80
  try {
@@ -131,24 +85,17 @@ export async function resolveCoordinatorMpreset(
131
85
  }
132
86
  throw error;
133
87
  }
134
- // Non-string input and explicit blank/whitespace strings can never name a
135
- // profile; only absent/null (handled above) means "no selection".
136
- if (typeof raw !== "string" || requested.length === 0) {
137
- return {
138
- ok: false,
139
- reason: "unknown_model_profile",
140
- mpreset: echoed,
141
- available_profiles: sortedProfileNames(profiles),
142
- };
143
- }
144
- const canonical = resolveCoordinatorModelProfileName(requested, profiles);
145
- if (!profiles.has(canonical)) {
146
- return {
147
- ok: false,
148
- reason: "unknown_model_profile",
149
- mpreset: echoed,
150
- available_profiles: sortedProfileNames(profiles),
151
- };
88
+ try {
89
+ const canonical = validateModelProfileName(requested, profiles);
90
+ return { ok: true, mpreset: canonical };
91
+ } catch (error) {
92
+ if (error instanceof UnknownModelProfileError)
93
+ return {
94
+ ok: false,
95
+ reason: "unknown_model_profile",
96
+ mpreset: error.details.requestedProfile.slice(0, MAX_ECHOED_MPRESET_LENGTH),
97
+ available_profiles: error.details.availableProfiles,
98
+ };
99
+ throw error;
152
100
  }
153
- return { ok: true, mpreset: canonical };
154
101
  }
@@ -96,14 +96,15 @@ If this raw bundled skill is loaded by GJC's native skill loader through `/skill
96
96
  ## Corrupt current-session state recovery
97
97
 
98
98
  When deep-interview detects its own current-session state is corrupt, tampered, unreadable, or stale on resume, run `gjc state clear --force --mode deep-interview` before reseeding or restarting. Scope the clear to the current session via `--session-id`, the command payload, or `GJC_SESSION_ID`; it clears only deep-interview state for that session and never clears other skills or sessions.
99
+ For ordinary diagnosis, run `gjc deep-interview sanity-check --session-id <id> --json` first, then use the issue's selector with `inspect` to read only the affected summary, topology, pending shell, round, facts, triggers, or floor. Repair a healthy, repairable local issue with its matching typed operation (`initialize-context`, `confirm-topology`, `record-answer`, or `apply-round-result`) and the current revision. Do not use grep/sed edits, direct `.gjc/` edits, generic envelope replacement, or `--force` as a repair mechanism; use generic `gjc state` only for the compatibility clear/handoff paths documented here.
99
100
 
100
101
  ## Phase 0: Resolve Ambiguity Threshold (blocking prerequisite)
101
102
 
102
103
  Complete this phase before Phase 1, before brownfield exploration, before GJC state persistence, before Round 0, and before any ambiguity scoring. Do not continue if the resolved threshold and source are unknown.
103
104
 
104
- 1. **Prefer pre-resolved native state**:
105
- - First inspect active deep-interview state with `gjc state deep-interview read --json`.
106
- - If state contains a finite numeric `threshold` and a non-empty `threshold_source`, use those values, set `<resolvedThreshold>`, `<resolvedThresholdPercent>`, and `<resolvedThresholdSource>`, and skip optional settings-file reads. This is the normal `/skill:deep-interview` path because the native hook already resolved settings quietly before loading the skill.
105
+ 1. **Inspect the native summary first**:
106
+ - Run `gjc deep-interview inspect --session-id <id> --selector summary --json`.
107
+ - If the summary contains a finite `threshold` and its preserved source is available from native state, use those values, set `<resolvedThreshold>`, `<resolvedThresholdPercent>`, and `<resolvedThresholdSource>`, and skip optional settings-file reads. This is the normal `/skill:deep-interview` path because the native hook already resolved settings quietly before loading the skill.
107
108
  2. **Only if native state lacks a resolved threshold, read threshold settings in runtime precedence order**:
108
109
  - YAML config first: read the **single** modern config path the environment selects — `$GJC_CODING_AGENT_DIR/config.yml` when `GJC_CODING_AGENT_DIR` is set, else `$GJC_CONFIG_DIR/agent/config.yml` when `GJC_CONFIG_DIR` is set, else `~/.gjc/agent/config.yml`. Do not cascade through the other YAML locations when the selected one is absent or invalid.
109
110
  - Then JSON settings: project settings `./.gjc/settings.json`, then user settings `[$GJC_CONFIG_DIR|~/.gjc]/settings.json`.
@@ -119,19 +120,19 @@ Deep Interview threshold: <resolvedThresholdPercent> (source: <resolvedThreshold
119
120
  ```
120
121
 
121
122
  5. **Carry threshold source forward mechanically**:
122
- - Substitute `<resolvedThreshold>`, `<resolvedThresholdPercent>`, and `<resolvedThresholdSource>` throughout the remaining instructions before continuing.
123
- - Include `threshold_source` in the first `gjc state write` payload and preserve it on later state updates; do not edit `.gjc/_session-{sessionid}/state` files directly unless an explicit force override is active.
123
+ - Pass threshold/source only as bounded setup evidence to `initialize-context`; native state owns the envelope, revision, receipt, lifecycle, derived ambiguity, and later updates. Do not reconstruct or submit a generic state envelope during normal flow.
124
124
  - Include both threshold and source in the final spec metadata.
125
125
  - Read any `language` object from active deep-interview state and carry `language.instruction` forward mechanically. If absent, default to English unless `{{ARGUMENTS}}` makes another user/session language obvious or the user explicitly requests another language. Do not add language-specific special cases.
126
126
 
127
127
  ## Phase 0.5: Suitability Gate
128
128
 
129
- Run this gate after the Phase 0 threshold marker and before Phase 1, brownfield exploration, `gjc state write`, Round 0, ambiguity scoring, or spec writing.
129
+ Run this gate after the Phase 0 threshold marker and before Phase 1, brownfield exploration, typed initialization, Round 0, ambiguity scoring, or spec writing.
130
130
 
131
131
  If the user request appended after this skill as the final `User:` line is already clear, bounded, low-risk, and asks for a quick fix, single change, known file/symbol edit, explicit command, or direct answer:
132
132
 
133
133
  1. **Stop deep-interview immediately**:
134
- - First inspect current-session state with `gjc state read --mode deep-interview --json` (include `--session-id <current-session-id>` when available).
134
+ - First inspect current-session state with `gjc deep-interview inspect --session-id <current-session-id> --selector summary --json`.
135
+ - For compatibility-only recovery where typed inspection is unavailable, `gjc state read --mode deep-interview --json` may confirm whether this narrow clear is safe; never use it to rebuild or replace state.
135
136
  - Clear through `gjc state clear --force --mode deep-interview --json` only when the state is a newly seeded empty interview: no recorded `rounds`, no `spec_path`, no `handoff_from`, no final/pending spec, and no user-confirmed topology.
136
137
  - If state already contains rounds, a spec path, handoff metadata, pending approval, or confirmed topology, do not clear it. Preserve the active interview and ask the user whether to continue, cancel, or explicitly clear the workflow.
137
138
  - Do not initialize deep-interview state.
@@ -181,46 +182,17 @@ Run this phase only when the active deep-interview state or invocation indicates
181
182
  - Preferred: pass the spec markdown **inline** to the native deep-interview write command (`--write … --spec "<markdown>"`) — no scratch file is needed. The CLI is the only sanctioned writer for `.gjc/_session-{sessionid}/specs`.
182
183
  - Only if a spec is too large to pass inline, stage it with the `write` tool to a system temp directory (`os.tmpdir()`/`$TMPDIR`, `/tmp`, `/var/tmp`) outside the project tree, then pass that path to `--spec`. The planning phase-boundary block tolerates these neutral temp writes; never stage interview artifacts inside the repo or under `.gjc/`, and do not improvise repo-relative scratch files.
183
184
 
184
- 4. **Initialize state** via `gjc state write`:
185
-
186
- ```json
187
- {
188
- "active": true,
189
- "current_phase": "interviewing",
190
- "state": {
191
- "interview_id": "<uuid>",
192
- "type": "greenfield|brownfield",
193
- "initial_idea": "<prompt-safe initial-context summary or user input>",
194
- "initial_context_summary": "<summary if oversized, else null>",
195
- "rounds": [],
196
- "established_facts": [],
197
- "current_ambiguity": 1.0,
198
- "threshold": <resolvedThreshold>,
199
- "threshold_source": "<resolvedThresholdSource>",
200
- "language": "<existing language object from active state, if present>",
201
- "trace_summary": "<bounded trace summary when --trace is active, else null>",
202
- "codebase_context": null,
203
- "topology": {
204
- "status": "pending|confirmed|legacy_missing",
205
- "confirmed_at": null,
206
- "components": [],
207
- "deferrals": [],
208
- "last_targeted_component_id": null
209
- },
210
- "ontology_snapshots": [],
211
- "auto_researched_rounds": [],
212
- "auto_answered_rounds": [],
213
- "lateral_reviews": [],
214
- "lateral_panel_failures": 0,
215
- "auto_answer_streak": 0,
216
- "refined_rounds": [],
217
- "closure_overrides": [],
218
- "restated_goal": null,
219
- "ambiguity_milestone": "initial",
220
- "architect_failures": 0
221
- }
222
- }
185
+ 4. **Initialize through a CLI-owned draft.** Create an `initialize-context` draft, edit only its bounded fields, check it, then consume it through the typed command. Never serialize a setup request, envelope, or derived values inline:
186
+ ```sh
187
+ gjc deep-interview draft create --for initialize-context --session-id <id> --json
188
+ # Capture draft_id and draft_revision from the response.
189
+ gjc deep-interview draft edit --draft-id <draft_id> --expected-draft-revision <draft_revision> --op set --path /type --value greenfield --json
190
+ gjc deep-interview draft edit --draft-id <draft_id> --expected-draft-revision <next_draft_revision> --op set --path /initial_idea --value "<prompt-safe idea>" --json
191
+ gjc deep-interview draft edit --draft-id <draft_id> --expected-draft-revision <next_draft_revision> --op set --path /threshold_source --value "<source>" --json
192
+ gjc deep-interview draft check --draft-id <draft_id> --json
193
+ gjc deep-interview initialize-context --draft-id <draft_id> --expected-draft-revision <latest_draft_revision> --json
223
194
  ```
195
+ `draft create` records the state base revision; every edit returns the next `draft_revision`. The native runtime validates, consumes, stamps, and owns the complete state. Retain the returned state revision for inspection only; mutations consume their own checked draft.
224
196
 
225
197
  5. **Announce the interview** to the user:
226
198
 
@@ -267,46 +239,20 @@ Options should include contextually relevant choices such as **Looks right**, **
267
239
 
268
240
  The Round 0 `ask` call MUST include `deepInterview.round = 0`, `deepInterview.component = "review-topology"`, `deepInterview.dimension = "topology"`, `deepInterview.intent_contract.items` containing the exact displayed locked-intent items, and `deepInterview.intent_contract.confirmation_options` listing only the displayed affirmative labels that lock the proposal (normally **Looks right**). The runtime recorder canonicalizes and locks this contract only when the user selects one of those labels; correction, deferral, free-text, and clarification answers never lock the pre-question proposal. Do not manually copy raw free text into intent evidence, and do not continue if this required recorder write fails.
269
241
 
270
- 3. **Lock topology into state** after the answer. Store a normalized component list and confirmation timestamp:
271
-
272
- ```json
273
- {
274
- "topology": {
275
- "status": "confirmed",
276
- "confirmed_at": "<ISO-8601 timestamp>",
277
- "components": [
278
- {
279
- "id": "component-slug",
280
- "name": "Component Name",
281
- "description": "Confirmed top-level outcome",
282
- "status": "active|deferred",
283
- "evidence": ["initial prompt phrase or brownfield citation"],
284
- "clarity_scores": {
285
- "goal": null,
286
- "constraints": null,
287
- "criteria": null,
288
- "context": null
289
- },
290
- "weakest_dimension": null
291
- }
292
- ],
293
- "deferrals": [
294
- {
295
- "component_id": "component-slug",
296
- "reason": "User-confirmed deferral reason",
297
- "confirmed_at": "<ISO-8601 timestamp>"
298
- }
299
- ],
300
- "last_targeted_component_id": null
301
- }
302
- }
242
+ 3. **Lock topology through a CLI-owned draft** after the answer. Create a `confirm-topology` draft, use a valueless append on a missing object-item array to add an `{}` scaffold, set scalar leaves, and check it. A valueless append on a missing scalar-item array initializes `[]`; on an existing scalar-item array, append still requires `--value` or `--value-file`. For a topology with zero deferrals, initialize `/deferred_components` with that valueless scalar-array append, then consume it:
243
+ ```sh
244
+ gjc deep-interview draft create --for confirm-topology --session-id <id> --json
245
+ gjc deep-interview draft edit --draft-id <draft_id> --expected-draft-revision <draft_revision> --op append --path /components --json
246
+ gjc deep-interview draft edit --draft-id <draft_id> --expected-draft-revision <next_draft_revision> --op set --path /components/0/id --value <component_id> --json
247
+ gjc deep-interview draft edit --draft-id <draft_id> --expected-draft-revision <next_draft_revision> --op set --path /components/0/name --value "<component name>" --json
248
+ gjc deep-interview draft edit --draft-id <draft_id> --expected-draft-revision <next_draft_revision> --op append --path /deferred_components --json
249
+ gjc deep-interview draft check --draft-id <draft_id> --json
250
+ gjc deep-interview confirm-topology --draft-id <draft_id> --expected-draft-revision <latest_draft_revision> --json
303
251
  ```
252
+ The recorder still persists `state.intent_contract` from the Round 0 `ask` answer. The runtime validates pending/legacy state, preserves declared ordering, detects conflicting confirmations, derives component state, and returns the new revision. Do not replace `topology` through `gjc state write`.
253
+ It contains the four exact categories `artifact`, `surface`, `integration`, and `constraint`; every item has a unique category-prefixed ID (for example `surface:review`). The recorder canonically sorts items, persists the full SHA-256 manifest digest, and binds confirmation to a redacted answer-hash reference. Before spec persistence, include every preserved locked ID literally in the final spec. For a proposed missing locked ID, ask one intent-review question through `ask`; the runtime records `pending` unless the user selects an exact approval option, and spec persistence/handoff fail closed for missing, stale, or unrecorded reduction-review evidence. Intent review authorizes only that output reduction, never execution or handoff.
304
254
 
305
- In the same Round 0 answer, the runtime recorder persists `state.intent_contract` version 1 from `deepInterview.intent_contract.items`. It contains the four exact categories `artifact`, `surface`, `integration`, and `constraint`; every item has a unique category-prefixed ID (for example `surface:review`) and a bounded non-empty statement. The recorder canonically sorts items, persists the full SHA-256 manifest digest, and binds confirmation to a redacted answer-hash reference. The confirmation answer locks this manifest before Round 1; later prose, inferred implementation detail, raw answer content, or a regenerated digest cannot replace it.
306
-
307
- Before spec persistence, include every preserved locked ID literally in the final spec. Additions and clarifications need no extra question; the runtime derives and persists a `not_required` review when every locked ID remains. For any proposed missing locked ID, ask one intent-review question through `ask` and include `deepInterview.intent_review` with the proposed `observed_items`, every `supporting_substitution`, and the exact `approval_options` labels that count as approval. The runtime recorder writes `pending` when the user does not approve and writes `approved` only when an approval option is selected, binding the review to the recorder-generated answer hash without storing raw answer text. Approved reductions require every removed ID to map to an observed replacement ID. Spec persistence and handoff fail closed for missing, pending, malformed, stale, or unrecorded reduction review evidence. Intent review approves only that output reduction and never authorizes execution or ralplan handoff.
308
-
309
- 4. **Legacy state migration:** When resuming an existing `deep-interview` state file that lacks `topology`, treat it as `"status": "legacy_missing"`. If no final `spec_path` exists yet, run Round 0 before the next ambiguity scoring pass and then continue with the existing transcript. If a final spec already exists, do not rewrite history; note in any handoff that topology was not captured for that legacy interview.
255
+ 4. **Legacy state migration:** When a resumed legacy interview lacks confirmed topology and no final `spec_path` exists, run Round 0 and use `confirm-topology` before the next ambiguity scoring pass. If a final spec already exists, do not rewrite history; note in any handoff that topology was not captured for that legacy interview.
310
256
 
311
257
  5. **Single-component pass-through:** If the user confirms one active component, Phase 2 proceeds with the existing flow while still carrying `topology.components[0]` into scoring and spec output.
312
258
 
@@ -370,9 +316,19 @@ Options should include contextually relevant choices plus free-text, translated/
370
316
 
371
317
  After applying `language.instruction` to the visible question, options, and generated rationale, apply the self-proofread once to new prose only (DIPP-5); preserve only the Round/Component/Targeting/Ambiguity line structure, fixed labels, numeric ambiguity value, component/target identifiers, and `deepInterview.*` metadata keys. Do not exempt generated natural-language rationale such as Why now.
372
318
 
373
- When calling `ask`, SHOULD include optional structured metadata so the runtime can record the round without manual state writes: `deepInterview.round_id?`, `deepInterview.round`, `deepInterview.component`, `deepInterview.dimension`, and `deepInterview.ambiguity`. Keep this metadata aligned with the visible Round/Component/Targeting/Ambiguity line; if metadata cannot be supplied, the legacy formatted question text remains the fallback.
319
+ When calling `ask`, SHOULD include optional structured metadata so the runtime can record the round without manual state writes: `deepInterview.round_id?`, `deepInterview.round`, `deepInterview.component`, `deepInterview.dimension`, and `deepInterview.ambiguity`. Keep this metadata aligned with the visible Round/Component/Targeting/Ambiguity line. Recorder-first remains mandatory. If the runtime did not record the answer shell, inspect `pending` or `round` to obtain its identity, then create a `record-answer` draft with that identity, edit scalar leaves, scaffold option arrays, and consume it:
320
+ ```sh
321
+ gjc deep-interview draft create --for record-answer --session-id <id> --round <n> --question-id <id> [--round-id <id>] [--component-id <id>] [--dimension <dimension>] --json
322
+ gjc deep-interview draft edit --draft-id <draft_id> --expected-draft-revision <draft_revision> --op set --path /question --value "<question>" --json
323
+ gjc deep-interview draft edit --draft-id <draft_id> --expected-draft-revision <next_draft_revision> --op append --path /answer/selected_options --json
324
+ gjc deep-interview draft edit --draft-id <draft_id> --expected-draft-revision <next_draft_revision> --op set --path /answer/selected_options/0 --value "<selected option>" --json
325
+ gjc deep-interview draft edit --draft-id <draft_id> --expected-draft-revision <next_draft_revision> --op set --path /answer/custom_input --null --json
326
+ gjc deep-interview draft check --draft-id <draft_id> --json
327
+ gjc deep-interview record-answer --draft-id <draft_id> --expected-draft-revision <latest_draft_revision> --json
328
+ ```
329
+ Do not recreate transcript, rounds, or generic state JSON.
374
330
 
375
- If the `ask` tool returns `clarificationQuestion`, treat it as a non-answer about the displayed choices. Answer the clarification briefly from the current interview context, then call `ask` again with the exact original question, options, and `deepInterview.*` metadata. A clarification bypasses Step 2b′ auto-answer, Step 2b″ free-text refine, Step 2c ambiguity scoring, Step 2d progress reporting, and Step 2e state updates; it must not be recorded as a round answer. This does not violate the one-question-per-round rule because the round remains unresolved until the user submits a real listed option or `Other` answer.
331
+ If the `ask` tool returns `clarificationQuestion`, treat it as a non-answer about the displayed choices. Answer the clarification briefly from the current interview context, then call `ask` again with the exact original question, options, and `deepInterview.*` metadata. A clarification bypasses Step 2b′ auto-answer, Step 2b″ free-text refine, Step 2c ambiguity scoring, Step 2d apply-and-report, and Step 2e soft-limit checks; it must not be recorded as a round answer. This does not violate the one-question-per-round rule because the round remains unresolved until the user submits a real listed option or `Other` answer.
376
332
 
377
333
  ### Step 2b′: Auto-Answer Opted-Out Questions
378
334
 
@@ -382,7 +338,7 @@ Auto-answer has a clarity cap: unless the architect confidence is `high` and unc
382
338
 
383
339
  ### Step 2b″: Refine Free-Text Answers
384
340
 
385
- When the user's answer is free-text that carries reasoning, constraints, or scope decisions, do not forward it to scoring as a lossy one-line label. First structure it into a compact interpretation using the canonical sections — **Decision**, **Reasoning**, **Constraints (user-stated)**, **Out of scope (user-stated)**, and **Codebase context (verified)** (omit empty sections) — then confirm with exactly one `ask` that nothing is lost or misrepresented. Apply `language.instruction` when present.
341
+ When the user's answer is free-text that carries reasoning, constraints, or scope decisions, do not forward it to scoring as a lossy one-line label. First structure it into a compact interpretation using the canonical sections — **Decision**, **Reasoning**, **Constraints (user-stated)**, **Out of scope (user-stated)**, and **Codebase context (verified)** (omit empty sections). Then confirm with exactly one `ask` that nothing is lost or misrepresented: the `ask` question body MUST render the full structured interpretation — every non-empty canonical section, verbatim — before the confirmation prompt. The user is approving that specific interpretation, so it must be visible inside the question body; never ask "does this capture it?" / "이 해석이 맞아?" without first displaying the interpretation itself. A confirmation `ask` whose body omits the interpretation it is asking about is a hard error: re-issue it with the interpretation shown. Apply `language.instruction` when present.
386
342
 
387
343
  Offer options such as **Send as-is**, **Add a constraint**, **Mark something out of scope**, **Add context**, and **Rewrite**, plus free-text. If the user picks anything other than "Send as-is", collect the exact missing text with one follow-up `ask` (never infer it from the option label), fold it into the structured interpretation, and re-confirm. Do not advance to scoring while the user is still saying something is missing.
388
344
 
@@ -406,24 +362,17 @@ Ambiguity-raising triggers:
406
362
 
407
363
  Use **mechanism A** for every ambiguity rise: a trigger LOWERS the affected component/dimension clarity score, and the existing weighted formula raises ambiguity. There is **no separate penalty term**; ambiguity remains bounded by the same greenfield/brownfield formula.
408
364
 
409
- **Deterministic ambiguity floor (runtime-enforced).** The runtime independently computes a code-level floor from persisted state and clamps every reported ambiguity to `max(reported, floor)` at write time — the scorer cannot under-report below what code can objectively measure:
410
-
411
- - `+0.10` per established fact marked `disputed` that has no `superseded_by` resolution (contradiction pressure)
412
- - `+0.05` per active topology component whose goal/constraints/criteria clarity is still unscored (gap pressure — persist `topology.components[].clarity_scores` every round or the floor blocks convergence)
413
- - `+0.05 × (auto-answered rounds / scored rounds)` (assumption dilution)
365
+ **Native ambiguity and floor.** Submit candidate component scores, trigger/fact operations, ontology evidence, targeting, and bookkeeping in `RoundResult`; the runtime alone derives weighted ambiguity, floor, effective ambiguity, milestone, ontology stability, trigger correspondence, rotation, and auto-resolution streak. Do not calculate, assert, or persist any of those derived values in the request.
414
366
 
415
- Cooperate with the floor rather than fight it:
416
- - Replacing an already-scored answer for the same round (a retraction/pivot) automatically marks that round's established facts as disputed; ambiguity rises mechanically even when no trigger is reported. Treat a floor-driven rise as trigger evidence and score the affected dimensions accordingly.
417
- - A disputed fact keeps the floor at or above `0.10` — above the default threshold — so convergence is blocked until the dispute is resolved: either the user re-confirms the original fact (set `disputed: false`) or the superseding decision is recorded as a new established fact and the old fact gets `superseded_by: <new fact id>`. Never delete the contradicted fact.
418
- - When the effective score was clamped upward, the persisted round carries `reported_ambiguity` (your raw score) and `ambiguity_floor`; report the floor and its dominant cause in the Step 2d table instead of pretending the raw score held.
367
+ The native floor retains contradiction pressure, unscored active-component pressure, and auto-answer dilution. A disputed fact blocks convergence until it is resolved through the typed fact operation; never delete contradicted history. When the response reports a clamp, show the returned floor and dominant cause rather than inventing a score.
419
368
 
420
369
  The rise is SILENT: no modal, no forced-resolution step, and no dedicated conflict UI. Surface it through the normal per-round report and by targeting the next question at the affected component/dimension.
421
370
 
422
- Structured scorer output is required. Include `triggers`, `trigger_status`, `affected_component`, `affected_dimension`, `prior_dimension_score`, `new_dimension_score`, `prior_ambiguity`, `new_ambiguity`, `evidence`, `contradicted_established_fact` when relevant, and `disputed_unresolved_rationale` when applicable.
371
+ Structured scorer output is required. Include `triggers`, `trigger_status`, `affected_component`, `affected_dimension`, `prior_dimension_score`, `new_dimension_score`, `evidence`, `contradicted_established_fact` when relevant, and `disputed_unresolved_rationale` when applicable. Do not include prior/new ambiguity, floor, effective ambiguity, or milestone assertions.
423
372
 
424
373
  Established-facts maintenance: promote stable confirmed decisions into `state.established_facts` with source/evidence; when a new answer contradicts an established fact, mark the fact disputed and preserve the contradicted fact instead of deleting it. When the user later confirms the new direction, record the superseding decision as a new established fact and set `superseded_by: <new fact id>` on the disputed fact — that is the only way to release the deterministic floor pressure while keeping the audit trail.
425
374
 
426
- TRANSITION VALIDATION: if a trigger is present, the affected dimension must not improve and overall ambiguity must rise vs the prior scored round, unless the trigger is explicitly marked disputed or unresolved with rationale.
375
+ TRANSITION VALIDATION: if a trigger is present, the affected dimension must not improve unless the trigger is explicitly marked disputed or unresolved with rationale. The runtime validates and returns the authoritative ambiguity transition.
427
376
 
428
377
  Convergence Pacing deferral: do not add a min-round floor, score-drop cap, confidence dampening, or other explicit pacing brake. Bidirectional scoring is the pacing mechanism.
429
378
 
@@ -464,7 +413,7 @@ Also identify:
464
413
  - weakest_dimension: the single lowest-confidence dimension for that component this round
465
414
  - weakest_dimension_rationale: one sentence explaining why this component/dimension pair is the highest-leverage target for the next question
466
415
  - component_scores: object keyed by component id, with per-dimension scores and gaps
467
- - structured_scorer_output: object containing triggers, trigger_status, affected_component, affected_dimension, prior_dimension_score, new_dimension_score, prior_ambiguity, new_ambiguity, evidence, contradicted_established_fact when relevant, and disputed_unresolved_rationale when applicable
416
+ - structured_scorer_output: object containing triggers, trigger_status, affected_component, affected_dimension, prior_dimension_score, new_dimension_score, evidence, contradicted_established_fact when relevant, and disputed_unresolved_rationale when applicable; do not include ambiguity, floor, or milestone values
468
417
 
469
418
  5. Ontology Extraction: Identify all key entities (nouns) discussed in the transcript.
470
419
 
@@ -479,85 +428,51 @@ For each entity provide:
479
428
  Respond as JSON. Include an additional "ontology" key containing the entities array alongside the dimension scores.
480
429
  ```
481
430
 
482
- **Calculate ambiguity:**
483
-
484
- Greenfield: `ambiguity = 1 - (goal × 0.40 + constraints × 0.30 + criteria × 0.30)`
485
- Brownfield: `ambiguity = 1 - (goal × 0.35 + constraints × 0.25 + criteria × 0.25 + context × 0.15)`
486
- Brownfield adds the 15% Context Clarity dimension (Goal/Constraint/Criteria become 35/25/25) because safely modifying existing code requires understanding the system being changed.
487
-
488
- **Calculate ontology stability:**
489
-
490
- **Round 1 special case:** For the first round, skip stability comparison. All entities are "new". Set stability_ratio = N/A. If any round produces zero entities, set stability_ratio = N/A (avoids division by zero).
491
-
492
- For rounds 2+, compare with the previous round's entity list:
493
- - `stable_entities`: entities present in both rounds with the same name
494
- - `changed_entities`: entities with different names but the same type AND >50% field overlap (treated as renamed, not new+removed)
495
- - `new_entities`: entities in this round not matched by name or fuzzy-match to any previous entity
496
- - `removed_entities`: entities in the previous round not matched to any current entity
497
- - `stability_ratio`: (stable + changed) / total_entities (0.0 to 1.0, where 1.0 = fully converged)
498
-
499
- This formula counts renamed entities (changed) toward stability. Renamed entities indicate the concept persists even if the name shifted — this is convergence, not instability. Two entities with different names but the same `type` and >50% field overlap should be classified as "changed" (renamed), not as one removed and one added.
500
-
501
- **Show your work:** Before reporting stability numbers, briefly list which entities were matched (by name or fuzzy) and which are new/removed. This lets the user sanity-check the matching.
502
-
503
- Store the ontology snapshot (entities + stability_ratio + matching_reasoning) in `state.ontology_snapshots[]`.
504
-
505
- ### Step 2d: Report Progress
506
-
507
- After scoring, show the user their progress:
431
+ **Ontology input:** provide entities, relationships, and concise reasoning in `RoundResult`. The runtime determines first-round/no-entity handling, exact-name stability, deterministic renamed matching, counts, ratio, and snapshot persistence. Report the returned ontology result; do not send caller-computed counts, ratios, or snapshots.
508
432
 
433
+ ### Step 2d: Apply Round Result and Report Progress
434
+ After `ask` has recorded (or `record-answer` has recovered) the answer shell, inspect `pending` or `round` for its identity. Create an `apply-round-result` draft, edit only bounded scorer evidence, check it, and consume it once:
435
+ ```sh
436
+ gjc deep-interview draft create --for apply-round-result --session-id <id> --round-key <round_key> --json
437
+ gjc deep-interview draft edit --draft-id <draft_id> --expected-draft-revision <draft_revision> --op set --path /global_scores/goal --value 0.8 --json
438
+ gjc deep-interview draft edit --draft-id <draft_id> --expected-draft-revision <next_draft_revision> --op append --path /component_updates --json
439
+ gjc deep-interview draft edit --draft-id <draft_id> --expected-draft-revision <next_draft_revision> --op set --path /component_updates/0/component_id --value <component_id> --json
440
+ gjc deep-interview draft check --draft-id <draft_id> --json
441
+ gjc deep-interview apply-round-result --draft-id <draft_id> --expected-draft-revision <latest_draft_revision> --json
509
442
  ```
510
- Round {n} complete.
443
+ `RoundResult` remains candidate scores, component updates, triggers, fact operations, ontology input, targeting, and bookkeeping only. The runtime performs one authoritative transaction, returns native-derived scoring and the new revision, and rejects stale, conflicting, malformed, or unrecoverable state. Never enrich a round through `gjc state write`.
511
444
 
512
- | Dimension | Score | Weight | Weighted | Gap |
513
- |-----------|-------|--------|----------|-----|
514
- | Goal | {s} | {w} | {s*w} | {gap or "Clear"} |
515
- | Constraints | {s} | {w} | {s*w} | {gap or "Clear"} |
516
- | Success Criteria | {s} | {w} | {s*w} | {gap or "Clear"} |
517
- | Context (brownfield) | {s} | {w} | {s*w} | {gap or "Clear"} |
518
- | **Ambiguity** | | | **{prior_score}% -> {score}% {up|down|flat}** | {if up: trigger name such as "A direct contradiction"} |
519
- | **Floor** (only when clamped) | | | **{floor}%** | {dominant cause: disputed fact / unscored component / auto-answer dilution} |
445
+ After the typed transaction succeeds, consume `<native>.native_projection` as the version 1 native projection. Render only the paths listed below; do not report candidate values, inspect-state values, locally calculated values, names, weights, gaps, thresholds, ratios, causes, or milestone transitions that the projection does not expose.
520
446
 
521
- **Topology:** Targeted {target_component_name} | Active: {active_component_count} | Deferred: {deferred_component_count} | Next rotation after: {last_targeted_component_id}
522
-
523
- **Ontology:** {entity_count} entities | Stability: {stability_ratio} | New: {new} | Changed: {changed} | Stable: {stable}
524
- **Milestone:** {prior_milestone} → {current_milestone}{milestone_transition ? " — lateral panel convened" : ""}
525
-
526
- **Next target:** {target_component_name} / {weakest_dimension} — {weakest_dimension_rationale}
527
-
528
- {score <= threshold ? "Clarity threshold met! Ready to proceed." : "Focusing next question on: {weakest_dimension}"}
447
+ ```text
448
+ Round {n} complete.
529
449
 
450
+ Ambiguity: prior {<native>.native_projection.prior_effective_ambiguity}; current {<native>.native_projection.effective_ambiguity} ({<native>.native_projection.direction})
451
+ Floor: {<native>.native_projection.floor}
452
+ Milestone: {<native>.native_projection.ambiguity_milestone}
453
+ Topology: target {<native>.native_projection.targeting.target_component_id}; active {<native>.native_projection.topology_counts.active}; deferred {<native>.native_projection.topology_counts.deferred}; total {<native>.native_projection.topology_counts.total}
454
+ Ontology: stable {<native>.native_projection.ontology_counts.stable}; changed {<native>.native_projection.ontology_counts.changed}; new {<native>.native_projection.ontology_counts.new}
455
+ Next target: {<native>.native_projection.targeting.target_component_id} / {<native>.native_projection.targeting.target_dimension}
456
+ Auto-answer streak: {<native>.native_projection.transition.auto_answer_streak}
530
457
  ```
531
458
 
532
- Apply `language.instruction` when present before showing this progress report so status text, gaps, and next-target phrasing stay in the preserved session language.
459
+ `prior_effective_ambiguity` may be `null` on the first scored round; show that returned value rather than inventing a prior percentage. The transition lifecycle and round identity remain available at `<native>.native_projection.transition.lifecycle` and `<native>.native_projection.transition.round_key` for control flow, but are not progress prose.
533
460
 
534
- Then apply the self-proofread once (DIPP-5) to narrative status text, generated prose cells, gaps, and next-target phrasing; preserve only table structure, fixed status labels, scores, weights, component ids, and trigger tokens.
461
+ Apply `language.instruction` when present before showing this progress report; apply the self-proofread once (DIPP-5) to narrative status text, generated prose cells, gaps, and next-target phrasing while preserving the real native projection paths and fixed report structure.
535
462
 
536
- ### Step 2e: Update State
537
-
538
- Update state in two phases. The `ask` answer is first recorded by the runtime as an `answered` shell. Scoring then enriches the same round record to `scored` with global scores, per-component `topology.components[].clarity_scores`, `topology.components[].weakest_dimension`, trigger metadata, established-facts changes, ontology snapshot, `topology.last_targeted_component_id`, `auto_researched_rounds`, `auto_answered_rounds`, and `architect_failures`. When `deepInterview` ask metadata is present, no manual per-round `gjc state write` is required for the answer shell; only scoring enrichment/state maintenance remains. When metadata is absent, use the legacy `gjc state write` path to persist the new round and never patch `.gjc/_session-{sessionid}/state` directly unless an explicit force override is active.
539
- Also recompute and persist `ambiguity_milestone` each round (detect band transitions for the Phase 3 panel), and persist `auto_answer_streak`, `refined_rounds`, `lateral_reviews`, and `lateral_panel_failures` alongside the existing fields.
540
-
541
- ### Step 2f: Check Tiered Confirmation Cadence
463
+ ### Step 2e: Check Tiered Confirmation Cadence
542
464
 
543
465
  Confirmation cadence is tiered by round, adopted from ouroboros's ooo interview, while the hard safety cap is retained:
544
466
 
545
467
  - **Rounds 1-3 (auto-continue)**: minimum context gathering — proceed to the next question without a "continue?" prompt.
546
- - **Rounds 4-15 (ask to continue)**: after each round, ask "Continue, or proceed with current clarity ({score}%)?" so the user controls depth.
547
- - **Rounds 16+ (diminishing-returns warning)**: keep asking "Continue?" but prefix a diminishing-returns warning: "We're at {n} rounds (ambiguity: {score}%); each further round yields less. Continue or proceed?"
468
+ - **Rounds 4-15 (ask to continue)**: after each round, ask "Continue, or proceed with current clarity ({<native>.native_projection.effective_ambiguity}%)?" so the user controls depth.
469
+ - **Rounds 16+ (diminishing-returns warning)**: keep asking "Continue?" but prefix a diminishing-returns warning: "We're at {n} rounds (ambiguity: {<native>.native_projection.effective_ambiguity}%); each further round yields less. Continue or proceed?"
548
470
  - **Round 3+ early exit**: still allow immediate exit if the user says "enough", "let's go", "build it".
549
- - **Round 100 (hard cap)**: "Maximum interview rounds reached. Proceeding with current clarity level ({score}%)." The tiered cadence never removes this hard safety cap.
471
+ - **Round 100 (hard cap)**: "Maximum interview rounds reached. Proceeding with current clarity level ({<native>.native_projection.effective_ambiguity}%)." The tiered cadence never removes this hard safety cap.
550
472
 
551
473
  ## Phase 3: Lateral Review Panel (milestone-triggered)
552
474
 
553
- The interview convenes a short multi-persona panel at **ambiguity-milestone transitions** instead of at fixed round numbers. Define milestone bands from the round's ambiguity score:
554
-
555
- | Band | Ambiguity |
556
- |------|-----------|
557
- | `initial` | > 0.60 |
558
- | `progress` | 0.60 ≥ a > 0.30 |
559
- | `refined` | 0.30 ≥ a > threshold |
560
- | `ready` | ≤ threshold |
475
+ The runtime returns the sole ready-first milestone mapping: `ready` when effective ambiguity is at or below the frozen threshold; otherwise `initial` above 0.60, `progress` above 0.30, and `refined` at or below 0.30. Use the returned milestone rather than recalculating bands.
561
476
 
562
477
  A transition occurs whenever the band changes versus the prior scored round — in either direction, since bidirectional scoring can move the band back up. On a transition, and also before synthesizing any agent-supplied answer (auto-research candidates, an auto-answer, or a code/brownfield auto-confirm that carries real interpretation), convene the panel before generating or asking the next question.
563
478
 
@@ -601,7 +516,7 @@ When ambiguity ≤ threshold (or hard cap / early exit):
601
516
 
602
517
  **4a. Closure / Acceptance Guard.** Even when ambiguity ≤ threshold, do not treat the math as completion. Run an independent readiness audit from the full main-session perspective (including explore findings, established facts, and triggers the scorer may not have fully weighed). Confirm every active topology component has goal/constraint/criteria coverage, no unresolved or disputed trigger remains on a path that matters, no disputed established fact lacks a `superseded_by` resolution, and no low-confidence auto-answer is standing in for user-confirmed truth above the clarity cap. If a material gap exists, explicitly override the gate to the user — "The math says ready, but I am not accepting it yet because {gap}" — and ask the single highest-impact follow-up, returning to Phase 2. Record any override in `state.closure_overrides`.
603
518
 
604
- **4b. Restate gate.** Once closure passes, collapse the agreed answers into ONE sentence goal that covers every active component, and confirm it with a single `ask`: "If someone read only this line, would they reach the same outcome you have in mind?" Offer **Yes, crystallize**, **Adjust wording**, and **Missing scope**, plus free-text, applying `language.instruction` when present. Because this gate has options, it MUST go through `ask`: do not print the Restate question and options as assistant prose with `Question:`/`Options:` labels. If the Restate gate was already printed that way, immediately call `ask` with the same question/options before accepting or waiting for any answer. On "Adjust wording" / "Missing scope", collect the exact correction with one follow-up `ask`, route it back through Step 2c scoring and established-facts maintenance (a correction can change ambiguity), then re-run closure and ask the Restate gate again. Cap at two loops; if alignment is not reached, return to Phase 2 with a targeted question instead of forcing a goal line. Persist the confirmed line as `state.restated_goal`.
519
+ **4b. Restate gate.** Once closure passes, collapse the agreed answers into ONE sentence goal that covers every active component, and confirm it with a single `ask` whose body MUST begin by stating that one-sentence goal verbatim, followed by: "If someone read only this line, would they reach the same outcome you have in mind?" The goal line must be visible inside the `ask` body; never ask the confirmation without first displaying the collapsed goal it refers to. Offer **Yes, crystallize**, **Adjust wording**, and **Missing scope**, plus free-text, applying `language.instruction` when present. Because this gate has options, it MUST go through `ask`: do not print the Restate question and options as assistant prose with `Question:`/`Options:` labels. If the Restate gate was already printed that way, immediately call `ask` with the same question/options before accepting or waiting for any answer. On "Adjust wording" / "Missing scope", collect the exact correction with one follow-up `ask`, route it back through Step 2c scoring and established-facts maintenance (a correction can change ambiguity), then re-run closure and ask the Restate gate again. Cap at two loops; if alignment is not reached, return to Phase 2 with a targeted question instead of forcing a goal line. Persist the confirmed line as `state.restated_goal`.
605
520
 
606
521
  1. **Generate the specification** using opus model with the prompt-safe transcript. If the full interview transcript or initial context is too large, include the summary plus all concrete decisions, acceptance criteria, unresolved gaps, and ontology snapshots; never overflow the prompt with raw oversized context.
607
522
  - Apply `language.instruction` when present so user-facing prose in the spec preserves the session language; keep code identifiers, file paths, commands, JSON/settings keys, and quoted source text unchanged.
@@ -657,7 +572,7 @@ Spec structure:
657
572
  {List stable confirmed decisions promoted into `state.established_facts`, including source round, evidence, and disputed status when any fact was contradicted.}
658
573
 
659
574
  ## Trigger Metadata
660
- {Summarize per-round trigger metadata: trigger label/status, affected component/dimension, prior -> new ambiguity direction, evidence, contradicted established fact when relevant, and disputed/unresolved rationale when applicable.}
575
+ {Summarize per-round trigger metadata: trigger label/status, affected component/dimension, native-returned effective ambiguity transition, evidence, contradicted established fact when relevant, and disputed/unresolved rationale when applicable.}
661
576
 
662
577
  ## Lateral Review Panel
663
578
  {Summarize convened panels: round, milestone transition or pre-answer trigger, personas dispatched, and the concrete findings folded into questions. Note any lateral_panel_failures.}
@@ -800,7 +715,7 @@ Skipping any stage is possible but reduces quality assurance:
800
715
  - Use `read/search/find exploration or a bounded read-only planner/architect subagent` for brownfield codebase exploration (run BEFORE asking user about codebase)
801
716
  - Use opus model (temperature 0.1) for ambiguity scoring — consistency is critical
802
717
  - Round 0 topology confirmation happens before ambiguity scoring; Phase 2 scoring must honor locked topology and rotate targeting across active components when more than one is present
803
- - Use `gjc state write` / `gjc state read` for interview state persistence; the initial and subsequent deep-interview state payloads must include `threshold_source` alongside `threshold`; do not edit `.gjc/_session-{sessionid}/state` directly without force override.
718
+ - Normal interview persistence uses CLI-owned drafts: `gjc deep-interview draft create|edit|show|check|rebase|discard`, followed by the matching typed command with `--draft-id --expected-draft-revision <latest_draft_revision> --json`. All public draft commands require standalone `--json`; value-taking flags use exactly `--name value`, while `--json` and `--null` take no value, and there is no public `draft consume` command. Use `--value`, standalone `--null`, and append scaffolds for bounded payload fields: valueless append on a missing object-item array appends `{}`, valueless append on a missing scalar-item array initializes `[]`, and appending to an existing scalar-item array requires `--value` or `--value-file`. Retain each returned `draft_revision`. `check` reports a stale state base but does not mutate; explicitly `rebase` only with the caller-observed `--to-state-revision` and then check again. Inline JSON request flags are compatibility-only. Never construct a full payload or generic `gjc state write --input` envelope. Generic `gjc state` remains compatibility/recovery-only for the explicit clear and final handoff paths; never edit `.gjc/_session-{sessionid}/state` directly without force override.
804
719
  - Use the GJC workflow CLI to save the final spec at `.gjc/_session-{sessionid}/specs/deep-interview-{slug}.md` exactly; do not use `write`, `edit`, or `ast_edit` directly on `.gjc/` paths without force override.
805
720
  - Use public GJC workflow entrypoints to bridge to ralplan, ultragoal, or team only after explicit execution approval — never implement directly. Implementation handoff defaults to ultragoal; reserve team for when tmux-based interactive worker parallelization is genuinely required.
806
721
  - The lateral-review panel spawns read-only persona subagents (Task tool) in parallel with independent context; it is an assist layer, never an executor and never the completion authority
@@ -942,7 +857,7 @@ Optional settings in `.gjc/settings.json`:
942
857
 
943
858
  ## Resume
944
859
 
945
- If interrupted, run `/skill:deep-interview` again. The skill resumes from GJC workflow state via `gjc state read`; do not read or edit `.gjc/_session-{sessionid}/state` files directly unless an explicit force override is active.
860
+ If interrupted, run `/skill:deep-interview` again. Resume through `gjc deep-interview inspect --session-id <id> --selector summary --json`, then selectively inspect topology, pending shells, recent scored rounds, facts, triggers, or floor as needed; do not read or edit `.gjc/_session-{sessionid}/state` files directly unless an explicit force override is active.
946
861
 
947
862
  ## Integration with staged team routing
948
863
 
@@ -34,6 +34,8 @@ For corrupt, tampered, unreadable, or stale current-session ralplan state, run `
34
34
 
35
35
  Ralplan is planning only. It may inspect context and draft plan/spec/proposal artifacts, but those remain `pending approval` until explicit current-turn or structured-UI execution approval. Before that approval, do not mutate product source, run mutation-oriented shell, commit, push, open PRs, invoke execution skills, or delegate implementation.
36
36
 
37
+ Explicitly naming `ultragoal` or `team` (including `/skill:` and `gjc` forms) counts as opting into execution for that skill — do not re-ask for the same consent.
38
+
37
39
  Persist planning artifacts and handoffs through the ralplan CLI writer, never direct `.gjc/` edits:
38
40
  Direct `write`, `edit`, or `ast_edit` calls against `.gjc/_session-{sessionid}/specs`, `.gjc/_session-{sessionid}/plans`, `.gjc/_session-{sessionid}/state`, or any other `.gjc/` path are forbidden unless an explicit force override is active.
39
41
 
@@ -82,7 +84,7 @@ The consensus workflow:
82
84
  - For every confirmed open item, embed the resolved outcome into the final plan under an **## Intent Reconciliation** section so the `pending approval` artifact records each decision; record any item the user explicitly defers as an open confirmation under that same section.
83
85
  d. Persist the reconciliation with `gjc ralplan --write --stage post-interview --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json`, then return the receipt/path plus a compact status (reconciled-clean / reconciled-with-revision / open-confirmations-pending) instead of pasting the full body.
84
86
  7. On reconciliation completion, re-check the review join gate (Critic `OKAY` plus Architect `CLEAR`/`APPROVE` for the same Planner artifact/pass), mark the plan `pending approval` unless explicit execution approval has already been captured, persist the ADR/final plan via `gjc ralplan --write --stage final --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT`, and do not directly edit `.gjc/_session-{sessionid}/plans`. Final plan must include ADR (Decision, Drivers, Alternatives considered, Why chosen, Consequences, Follow-ups) and, when present, the **## Intent Reconciliation** section.
85
- 8. **Always** present the finalized plan via the `ask` tool (regardless of `--interactive`) with `workflowGate: { stage: "ralplan", kind: "approval" }` on the final question so RPC/headless clients receive a `ralplan`/`approval` workflow gate, not a deep-interview question gate. Use these options:
87
+ 8. **Final approval gate (with explicit-execution exception):** If the user already explicitly named an execution skill in the current turn or via the structured approval UI (`ultragoal`, `/skill:ultragoal`, `gjc ultragoal`, `team`, `/skill:team`, `gjc team`, or "Approve execution via ultragoal/team"), that is execution approval — skip the re-ask and proceed to step 9 with that skill. Otherwise, **always** present the finalized plan via the `ask` tool (regardless of `--interactive`) with `workflowGate: { stage: "ralplan", kind: "approval" }` on the final question so RPC/headless clients receive a `ralplan`/`approval` workflow gate, not a deep-interview question gate. Use these options:
86
88
  - **Refine further** — re-run the consensus loop / request changes, then return here
87
89
  - **Approve execution via ultragoal (Recommended)** — goal-tracked autonomous execution
88
90
  - **Approve execution via team** — only when tmux-based interactive worker parallelization is required
package/src/edit/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@gajae-code/agent-core";
2
- import { prompt } from "@gajae-code/utils";
2
+ import { $pickenv, prompt } from "@gajae-code/utils";
3
3
  import type * as z from "zod/v4";
4
4
  import {
5
5
  executeHashlineSingle,
@@ -352,11 +352,8 @@ export class EditTool implements AgentTool<TInput> {
352
352
  readonly #pendingDeferredFetches = new Map<string, AbortController>();
353
353
 
354
354
  constructor(private readonly session: ToolSession) {
355
- const {
356
- PI_EDIT_FUZZY: editFuzzy = "auto",
357
- PI_EDIT_FUZZY_THRESHOLD: editFuzzyThreshold = "auto",
358
- PI_EDIT_VARIANT: envEditVariant = "auto",
359
- } = Bun.env;
355
+ const { PI_EDIT_FUZZY: editFuzzy = "auto", PI_EDIT_FUZZY_THRESHOLD: editFuzzyThreshold = "auto" } = Bun.env;
356
+ const envEditVariant = $pickenv("GJC_EDIT_VARIANT", "PI_EDIT_VARIANT") ?? "auto";
360
357
 
361
358
  this.#editMode = resolveConfiguredEditMode(envEditVariant);
362
359
  this.#allowFuzzy = resolveAllowFuzzy(session, editFuzzy);