@gajae-code/coding-agent 0.4.0 → 0.4.2
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 +18 -0
- package/dist/types/commands/ultragoal.d.ts +1 -0
- package/dist/types/config/settings-schema.d.ts +11 -3
- package/dist/types/gjc-runtime/launch-tmux.d.ts +1 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +29 -0
- package/dist/types/harness-control-plane/finalize.d.ts +8 -0
- package/dist/types/harness-control-plane/receipts.d.ts +16 -1
- package/dist/types/harness-control-plane/types.d.ts +9 -1
- package/dist/types/modes/rpc/rpc-client.d.ts +11 -1
- package/dist/types/reminders/star-reminder.d.ts +115 -0
- package/dist/types/session/agent-session.d.ts +18 -0
- package/examples/extensions/README.md +20 -41
- package/package.json +7 -7
- package/src/cli/grep-cli.ts +1 -1
- package/src/commands/harness.ts +42 -3
- package/src/commands/ultragoal.ts +1 -0
- package/src/config/settings-schema.ts +13 -3
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +19 -23
- package/src/defaults/gjc/skills/ralplan/SKILL.md +7 -7
- package/src/defaults/gjc/skills/team/SKILL.md +10 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +3 -2
- package/src/gjc-runtime/launch-tmux.ts +25 -2
- package/src/gjc-runtime/team-runtime.ts +78 -3
- package/src/gjc-runtime/ultragoal-guard.ts +18 -2
- package/src/gjc-runtime/ultragoal-runtime.ts +240 -30
- package/src/harness-control-plane/finalize.ts +84 -0
- package/src/harness-control-plane/owner.ts +13 -0
- package/src/harness-control-plane/receipts.ts +39 -1
- package/src/harness-control-plane/types.ts +25 -1
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/modes/interactive-mode.ts +26 -0
- package/src/modes/rpc/rpc-client.ts +22 -0
- package/src/prompts/system/system-prompt.md +9 -0
- package/src/reminders/star-reminder.ts +422 -0
- package/src/session/agent-session.ts +79 -13
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
} from "./skill-settings-defaults";
|
|
10
10
|
|
|
11
11
|
/** Unified settings schema - single source of truth for all settings.
|
|
12
|
-
* Unified settings schema - single source of truth for all settings.
|
|
13
12
|
*
|
|
14
13
|
* Each setting is defined once here with:
|
|
15
14
|
* - Type and default value
|
|
@@ -1010,6 +1009,16 @@ export const SETTINGS_SCHEMA = {
|
|
|
1010
1009
|
},
|
|
1011
1010
|
},
|
|
1012
1011
|
|
|
1012
|
+
"starReminder.enabled": {
|
|
1013
|
+
type: "boolean",
|
|
1014
|
+
default: true,
|
|
1015
|
+
ui: {
|
|
1016
|
+
tab: "interaction",
|
|
1017
|
+
label: "GitHub Star Reminder",
|
|
1018
|
+
description: "Show the interactive GitHub star reminder when gh is authenticated",
|
|
1019
|
+
},
|
|
1020
|
+
},
|
|
1021
|
+
|
|
1013
1022
|
collapseChangelog: {
|
|
1014
1023
|
type: "boolean",
|
|
1015
1024
|
default: false,
|
|
@@ -1089,11 +1098,12 @@ export const SETTINGS_SCHEMA = {
|
|
|
1089
1098
|
// Context promotion
|
|
1090
1099
|
"contextPromotion.enabled": {
|
|
1091
1100
|
type: "boolean",
|
|
1092
|
-
default:
|
|
1101
|
+
default: false,
|
|
1093
1102
|
ui: {
|
|
1094
1103
|
tab: "context",
|
|
1095
1104
|
label: "Auto-Promote Context",
|
|
1096
|
-
description:
|
|
1105
|
+
description:
|
|
1106
|
+
"Promote to a larger-context model on context overflow instead of compacting (off by default; opt in to enable)",
|
|
1097
1107
|
},
|
|
1098
1108
|
},
|
|
1099
1109
|
|
|
@@ -530,28 +530,24 @@ After the spec is written, mark it `pending approval` and present execution opti
|
|
|
530
530
|
|
|
531
531
|
**Options:**
|
|
532
532
|
|
|
533
|
-
1. **Refine with ralplan consensus (Recommended)**
|
|
534
|
-
- Description: "Consensus-refine this spec with Planner/Architect/Critic, then stop for explicit execution approval. Maximum quality."
|
|
535
|
-
- Action: Only after the user selects this option, invoke `/skill:ralplan
|
|
536
|
-
- Pipeline: `deep-interview spec → explicit approval to refine → ralplan
|
|
533
|
+
1. **Refine with ralplan consensus (Recommended — default for almost all specs)**
|
|
534
|
+
- Description: "Consensus-refine this spec with Planner/Architect/Critic, then stop for explicit execution approval. Maximum quality. Prefer this unless the spec is already implementation-ready and trivially simple."
|
|
535
|
+
- Action: Only after the user selects this option, invoke `/skill:ralplan` with the spec file path as context. Ralplan is already the Planner → Architect → Critic consensus workflow, so no extra slash-skill flags are required or supported. When consensus completes and produces a plan in `.gjc/plans/`, stop with that plan marked `pending approval`; do not automatically invoke execution or any other execution skill.
|
|
536
|
+
- Pipeline: `deep-interview spec → explicit approval to refine → ralplan → pending approval → separate execution approval`
|
|
537
537
|
|
|
538
|
-
2. **Execute with
|
|
539
|
-
- Description: "
|
|
540
|
-
- Action: Invoke `/skill:
|
|
538
|
+
2. **Execute with ultragoal (only when spec is already implementation-ready and really simple)**
|
|
539
|
+
- Description: "Goal-tracked autonomous execution — drives the spec to completion with verification. Skip ralplan refinement only when the spec is concrete, low-risk, and trivially small."
|
|
540
|
+
- Action: Invoke `/skill:ultragoal` with the spec file path as context only after the user explicitly selects this execution option. The spec replaces ultragoal planning input. Recommend this only when the spec needs no further planning; otherwise route through ralplan refinement first.
|
|
541
541
|
|
|
542
|
-
3. **Execute with team**
|
|
543
|
-
- Description: "
|
|
544
|
-
- Action: Invoke `/skill:team` with the spec file path as the
|
|
542
|
+
3. **Execute with team (only when implementation-ready, simple, AND tmux parallelization is required)**
|
|
543
|
+
- Description: "N coordinated parallel agents in tmux — only when the spec is already implementation-ready and genuinely needs tmux-based interactive worker parallelization."
|
|
544
|
+
- Action: Invoke `/skill:team` with the spec file path as the shared plan only after the user explicitly selects this option. Reserve this for the narrow case where the spec is simple/ready and tmux interactive parallel workers are actually needed; otherwise prefer ralplan refinement, then ultragoal.
|
|
545
545
|
|
|
546
|
-
4. **
|
|
547
|
-
- Description: "N coordinated parallel agents — fastest execution for large specs"
|
|
548
|
-
- Action: Invoke `/skill:team` with the spec file path as the shared plan.
|
|
549
|
-
|
|
550
|
-
5. **Refine further**
|
|
546
|
+
4. **Refine further**
|
|
551
547
|
- Description: "Continue interviewing to improve clarity (current: {score}%)"
|
|
552
548
|
- Action: Return to Phase 2 interview loop.
|
|
553
549
|
|
|
554
|
-
**IMPORTANT:** On explicit execution selection, **MUST** use the chosen bundled GJC workflow skill entrypoint (`/skill:ralplan
|
|
550
|
+
**IMPORTANT:** On explicit execution selection, **MUST** use the chosen bundled GJC workflow skill entrypoint (`/skill:ralplan`, `/skill:ultragoal`, or `/skill:team`) inside the agent session. `gjc ralplan` is a native CLI that accepts the documented skill flags and seeds local `.gjc/state` receipts; agent sessions should still drive the consensus loop through `/skill:ralplan`. Implementation handoff defaults to `/skill:ultragoal`; `/skill:team` is reserved for when tmux-based interactive worker parallelization is genuinely required, and `gjc team` is a native tmux runtime command used only when the Team workflow explicitly requires the CLI runtime. Do NOT implement directly. The deep-interview agent is a requirements agent, not an execution agent. If oversized initial context was summarized, pass the spec and prompt-safe summary forward, not the raw oversized source material. Without explicit execution selection, stop with the spec marked `pending approval`.
|
|
555
551
|
|
|
556
552
|
### Phase 5b: Handoff before chain
|
|
557
553
|
|
|
@@ -577,7 +573,7 @@ Stage 1: Deep Interview Stage 2: ralplan consensus Stage 3: Separ
|
|
|
577
573
|
┌─────────────────────┐ ┌───────────────────────────┐ ┌──────────────────────┐
|
|
578
574
|
│ Socratic Q&A │ │ Planner creates plan │ │ User chooses if/how │
|
|
579
575
|
│ Ambiguity scoring │───>│ Architect reviews │───>│ execution proceeds │
|
|
580
|
-
│ Challenge agents │ │ Critic validates │ │ via
|
|
576
|
+
│ Challenge agents │ │ Critic validates │ │ via ultragoal (default) │
|
|
581
577
|
│ Spec crystallization│ │ Loop until consensus │ │ no auto-handoff │
|
|
582
578
|
│ Gate: ≤<resolvedThresholdPercent> ambiguity│ │ ADR + RALPLAN-DR summary │ │ │
|
|
583
579
|
└─────────────────────┘ └───────────────────────────┘ └──────────────────────┘
|
|
@@ -604,7 +600,7 @@ Skipping any stage is possible but reduces quality assurance:
|
|
|
604
600
|
- 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
|
|
605
601
|
- 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/state` directly without force override.
|
|
606
602
|
- Use the GJC workflow CLI to save the final spec at `.gjc/specs/deep-interview-{slug}.md` exactly; do not use `write`, `edit`, or `ast_edit` directly on `.gjc/` paths without force override.
|
|
607
|
-
- Use public GJC workflow entrypoints to bridge to ralplan
|
|
603
|
+
- 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.
|
|
608
604
|
- Challenge agent modes are prompt injections, not separate agent spawns
|
|
609
605
|
- Use internal fragment auto-modes only at their documented hooks: `auto-research-greenfield.md` between Step 2a and 2b for greenfield `research: true` questions, and `auto-answer-uncertain.md` as Step 2b′ after `ask` resolves and before scoring.
|
|
610
606
|
- Fragment auto-modes are loaded on demand as `kind: "skill-fragment"`; they are not public workflow skills, not slash-command/discoverable, and not `skill://` registrations.
|
|
@@ -734,7 +730,7 @@ Why bad: 45% ambiguity means nearly half the requirements are unclear. The mathe
|
|
|
734
730
|
- [ ] Spec includes: topology, goal, constraints, acceptance criteria, clarity breakdown, transcript
|
|
735
731
|
- [ ] Execution bridge presented via the `ask` tool
|
|
736
732
|
- [ ] Selected execution mode invoked via public GJC workflow entrypoint only after explicit execution approval (never direct implementation)
|
|
737
|
-
- [ ] If 3-stage pipeline selected: ralplan
|
|
733
|
+
- [ ] If 3-stage pipeline selected: `/skill:ralplan` invoked with the spec as context, then stopped with the consensus plan marked `pending approval` until the user explicitly approves execution
|
|
738
734
|
- [ ] State cleaned up after approved workflow handoff
|
|
739
735
|
- [ ] Brownfield confirmation questions cite repo evidence (file/path/pattern) before asking the user to decide
|
|
740
736
|
- [ ] Scope-fuzzy tasks can trigger ontology-style questioning to stabilize the core entity before feature elaboration
|
|
@@ -783,7 +779,7 @@ Team routing: "Your request is quite open-ended. Would you like to run a deep in
|
|
|
783
779
|
[Yes, interview first] [No, expand directly]
|
|
784
780
|
```
|
|
785
781
|
|
|
786
|
-
If the user chooses interview, team routing invokes `/skill:deep-interview`. When the interview completes and the user selects
|
|
782
|
+
If the user chooses interview, team routing invokes `/skill:deep-interview`. When the interview completes and the user selects an execution path (ultragoal by default, or team when tmux-based interactive parallelization is required), the spec becomes Phase 0 output and the chosen workflow proceeds from the approved spec.
|
|
787
783
|
|
|
788
784
|
## Approval-Gated Pipeline: deep-interview → ralplan → pending approval
|
|
789
785
|
|
|
@@ -794,17 +790,17 @@ The recommended refinement path chains clarity and feasibility gates, then stops
|
|
|
794
790
|
→ Socratic Q&A until ambiguity ≤ <resolvedThresholdPercent>
|
|
795
791
|
→ Spec written to .gjc/specs/deep-interview-{slug}.md
|
|
796
792
|
→ User explicitly selects "Refine with ralplan consensus"
|
|
797
|
-
→ /skill:ralplan
|
|
793
|
+
→ /skill:ralplan (spec as input)
|
|
798
794
|
→ Planner creates implementation plan from spec
|
|
799
795
|
→ Architect reviews for architectural soundness
|
|
800
796
|
→ Critic validates quality and testability
|
|
801
797
|
→ Loop until consensus (max 5 iterations)
|
|
802
798
|
→ Consensus plan written to .gjc/plans/
|
|
803
799
|
→ Stop with the consensus plan marked pending approval
|
|
804
|
-
→ Only a separate explicit execution approval may invoke team
|
|
800
|
+
→ Only a separate explicit execution approval may invoke execution (ultragoal by default; team only when tmux-based interactive worker parallelization is required)
|
|
805
801
|
```
|
|
806
802
|
|
|
807
|
-
**The ralplan skill receives the spec
|
|
803
|
+
**The ralplan skill receives the spec as context through `/skill:ralplan`** because ralplan is already the GJC Planner → Architect → Critic consensus workflow. The consensus plan includes:
|
|
808
804
|
- RALPLAN-DR summary (Principles, Decision Drivers, Options)
|
|
809
805
|
- ADR (Decision, Drivers, Alternatives, Why chosen, Consequences)
|
|
810
806
|
- Testable acceptance criteria (inherited from deep-interview spec)
|
|
@@ -61,7 +61,7 @@ The consensus workflow:
|
|
|
61
61
|
- Viable Options (>=2) with bounded pros/cons
|
|
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
|
-
2. **User feedback** *(--interactive only)*: If `--interactive` is set, use `
|
|
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
65
|
3. **Architect** reviews for architectural soundness and must provide the strongest steelman antithesis, at least one real tradeoff tension, and (when possible) synthesis — **await completion before step 4**. In deliberate mode, Architect should explicitly flag principle violations.
|
|
66
66
|
- The Architect agent/subagent must persist its review with `gjc ralplan --write --stage architect --stage_n <N> --artifact "..." --json`, then return the receipt/path plus compact verdict/status (`CLEAR`/`WATCH`/`BLOCK`, `APPROVE`/`COMMENT`/`REQUEST CHANGES`) instead of pasting the full review body.
|
|
67
67
|
4. **Critic** evaluates against quality criteria — run only after step 3 completes. Critic must enforce principle-option consistency, fair alternatives, risk mitigation clarity, testable acceptance criteria, and concrete verification steps. In deliberate mode, Critic must reject missing/weak pre-mortem or expanded test plan.
|
|
@@ -74,9 +74,9 @@ The consensus workflow:
|
|
|
74
74
|
d. Return to Critic evaluation
|
|
75
75
|
e. Repeat this loop until Critic returns `APPROVE` or 5 iterations are reached
|
|
76
76
|
f. If 5 iterations are reached without `APPROVE`, present the best version to the user
|
|
77
|
-
6. On Critic approval, 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 "..."`, and do not directly edit `.gjc/plans`. *(--interactive only)* If `--interactive` is set, use `
|
|
78
|
-
7. *(--interactive only)* User chooses: Approve team execution, Request changes, or Reject
|
|
79
|
-
8. *(--interactive only)* On approval: invoke `/skill:
|
|
77
|
+
6. On Critic approval, 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 "..."`, and do not directly edit `.gjc/plans`. *(--interactive only)* If `--interactive` is set, use the `ask` tool to present the plan with approval options (Approve execution via ultragoal (Recommended) / Approve execution via team (only when tmux-based interactive worker parallelization is required) / Compact then return for execution approval / Request changes / Reject). Final plan must include ADR (Decision, Drivers, Alternatives considered, Why chosen, Consequences, Follow-ups). Otherwise, output the final plan and stop before any mutation or delegation.
|
|
78
|
+
7. *(--interactive only)* User chooses: Approve ultragoal execution (recommended), Approve team execution (tmux parallelization only), Request changes, or Reject
|
|
79
|
+
8. *(--interactive only)* On approval: invoke `/skill:ultragoal` for execution by default; invoke `/skill:team` only when the user explicitly needs tmux-based interactive worker parallelization -- never implement directly
|
|
80
80
|
|
|
81
81
|
Before invoking `/skill:team` or `/skill:ultragoal`, mark ralplan ready for handoff so the skill tool's chain guard permits the transition:
|
|
82
82
|
|
|
@@ -121,7 +121,7 @@ Set `--planner-resumable true` only when the parent session is provably persiste
|
|
|
121
121
|
|
|
122
122
|
### Why the Gate Exists
|
|
123
123
|
|
|
124
|
-
Execution
|
|
124
|
+
Execution skills (`ultragoal` and `team`) drive implementation rather than scope discovery. When launched on a vague request like "team improve the app", agents have no clear target — they waste cycles on scope discovery that should happen during planning, often delivering partial or misaligned work that requires rework.
|
|
125
125
|
|
|
126
126
|
The ralplan-first gate intercepts underspecified execution requests and redirects them through the ralplan consensus planning workflow. This ensures:
|
|
127
127
|
- **Explicit scope**: A PRD defines exactly what will be built
|
|
@@ -177,8 +177,8 @@ The gate auto-passes when it detects **any** concrete signal. You do not need al
|
|
|
177
177
|
- **Architect** reviews for soundness
|
|
178
178
|
- **Critic** validates quality and testability
|
|
179
179
|
5. On consensus approval, user chooses execution path:
|
|
180
|
-
- **
|
|
181
|
-
- **team**:
|
|
180
|
+
- **ultragoal**: goal-tracked autonomous execution with verification (recommended default)
|
|
181
|
+
- **team**: N coordinated parallel agents in tmux — only when tmux-based interactive worker parallelization is required
|
|
182
182
|
6. Execution begins with a clear, bounded plan
|
|
183
183
|
|
|
184
184
|
### Troubleshooting
|
|
@@ -124,6 +124,15 @@ When `$team` is used as a follow-up mode from ralplan, carry forward the approve
|
|
|
124
124
|
- explain why each lane exists (delivery, verification, specialist support)
|
|
125
125
|
- include an explicit launch hint (`gjc team "<task>"` / `$team "<task>"`) for the coordinated worker run; mention `$ultragoal` as the default durable follow-up/ledger path; mention a later separate Single-owner execution follow-up only when explicitly requested or genuinely needed as a fallback
|
|
126
126
|
- if the ideal role is unavailable, choose the closest role from the roster and say so
|
|
127
|
+
- For multi-worker follow-up execution, do not pass an inline "Split lanes: A..., B..." sentence as the whole team task. `gjc team` rejects ambiguous inline lane splits because they previously caused every worker to receive the same broad task. Use explicit markdown lane sections instead:
|
|
128
|
+
```md
|
|
129
|
+
### Lane A — Delivery
|
|
130
|
+
Implement delivery-only changes and evidence.
|
|
131
|
+
|
|
132
|
+
### Lane B — Verification
|
|
133
|
+
Add focused tests and smoke evidence.
|
|
134
|
+
```
|
|
135
|
+
Explicit `### Lane <id> — <title>` sections are converted into distinct worker-owned initial tasks.
|
|
127
136
|
|
|
128
137
|
## Current Runtime Behavior (As Implemented)
|
|
129
138
|
|
|
@@ -134,7 +143,7 @@ When `$team` is used as a follow-up mode from ralplan, carry forward the approve
|
|
|
134
143
|
3. Initialize team state:
|
|
135
144
|
- `.gjc/state/team/<team>/config.json`
|
|
136
145
|
- `.gjc/state/team/<team>/manifest.v2.json`
|
|
137
|
-
- `.gjc/state/team/<team>/tasks/task
|
|
146
|
+
- `.gjc/state/team/<team>/tasks/task-*.json` (one per explicit lane section, otherwise one worker-owned compatibility task per worker)
|
|
138
147
|
- `.gjc/state/team/<team>/mailbox/worker-1.json`
|
|
139
148
|
- `.gjc/state/team/<team>/workers/<worker>/status.json`
|
|
140
149
|
- `.gjc/state/team/<team>/workers/<worker>/lifecycle.json`
|
|
@@ -96,11 +96,12 @@ Loop until `gjc ultragoal status` reports all goals complete:
|
|
|
96
96
|
7. Before any `--status complete` checkpoint, run the mandatory final cleanup/review gate below. In aggregate mode, do **not** call `goal({"op":"complete"})` for intermediate stories; checkpoint each story with a fresh `goal({"op":"get"})` snapshot whose aggregate objective is still `active`. On the final story, use the same fresh active snapshot to create the final aggregate receipt first; only after that receipt exists may `goal({"op":"complete"})` run.
|
|
97
97
|
8. Checkpoint the durable ledger with that fresh active snapshot. Complete checkpoints require `--quality-gate-json`; the runtime hook rejects closure without a clean architect review:
|
|
98
98
|
`gjc ultragoal checkpoint --goal-id <id> --status complete --evidence "<evidence>" --gjc-goal-json <goal-get-json-or-path> --quality-gate-json <quality-gate-json-or-path>`
|
|
99
|
+
A successful complete checkpoint is story completion, not automatic run completion. Read the checkpoint output: when it prints `Next ultragoal goal: <id>`, continue that active story under the same aggregate GJC goal; when it prints `All ultragoal goals are complete`, the durable run is terminal. `gjc ultragoal complete-goals` remains the supported manual next-story command if continuation output was missed.
|
|
99
100
|
9. If blocked or failed, checkpoint failure:
|
|
100
101
|
`gjc ultragoal checkpoint --goal-id <id> --status failed --evidence "<blocker/evidence>"`
|
|
101
|
-
|
|
102
|
+
10. For legacy per-story completed-goal blockers, preserve the non-terminal blocker with:
|
|
102
103
|
`gjc ultragoal checkpoint --goal-id <id> --status blocked --evidence "<completed legacy GJC goal blocks goal create in this thread>" --gjc-goal-json <goal-get-json-or-path>`
|
|
103
|
-
|
|
104
|
+
11. Resume failed goals with `gjc ultragoal complete-goals --retry-failed`.
|
|
104
105
|
|
|
105
106
|
## Dynamic steering
|
|
106
107
|
|
|
@@ -48,6 +48,7 @@ export interface TmuxLaunchContext {
|
|
|
48
48
|
currentBranch?: string | null;
|
|
49
49
|
existingBranchSessionName?: string | null;
|
|
50
50
|
project?: string | null;
|
|
51
|
+
diagnosticWriter?: (message: string) => void;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
export interface TmuxSpawnResult {
|
|
@@ -120,6 +121,16 @@ function isInteractiveRootLaunch(parsed: Args, tty: TtyState): boolean {
|
|
|
120
121
|
);
|
|
121
122
|
}
|
|
122
123
|
|
|
124
|
+
function isBunVirtualPath(value: string | undefined): boolean {
|
|
125
|
+
return value?.startsWith("/$bunfs/") === true;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function formatTmuxLaunchDiagnostic(stage: string, stderr?: string): string {
|
|
129
|
+
const detail = stderr?.trim();
|
|
130
|
+
const suffix = detail ? ` ${detail.slice(0, 240)}` : "";
|
|
131
|
+
return `gjc --tmux failed after creating tmux session: ${stage}.${suffix}\n`;
|
|
132
|
+
}
|
|
133
|
+
|
|
123
134
|
function shellQuote(value: string): string {
|
|
124
135
|
if (value.length === 0) return "''";
|
|
125
136
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
@@ -148,6 +159,9 @@ export function applyGjcTmuxProfile(context: GjcTmuxProfileContext): GjcTmuxProf
|
|
|
148
159
|
function resolveCurrentGjcCommand(context: CommandResolutionContext): string[] {
|
|
149
160
|
const entrypoint = context.argv[1];
|
|
150
161
|
if (!entrypoint) return ["gjc"];
|
|
162
|
+
if (isBunVirtualPath(entrypoint)) {
|
|
163
|
+
return isBunVirtualPath(context.execPath) ? ["gjc"] : [context.execPath];
|
|
164
|
+
}
|
|
151
165
|
const resolvedEntrypoint = path.isAbsolute(entrypoint) ? entrypoint : path.resolve(context.cwd, entrypoint);
|
|
152
166
|
if (entrypoint.endsWith(".ts") || entrypoint.endsWith(".js") || entrypoint.endsWith(".mjs")) {
|
|
153
167
|
return [context.execPath, resolvedEntrypoint];
|
|
@@ -264,10 +278,19 @@ export function launchDefaultTmuxIfNeeded(context: TmuxLaunchContext): boolean {
|
|
|
264
278
|
});
|
|
265
279
|
if (profile.failures.length > 0) {
|
|
266
280
|
cleanupCreatedTmuxSession(plan, spawnSync, options);
|
|
267
|
-
|
|
281
|
+
const failure =
|
|
282
|
+
profile.failures.find(item => item.command.args.includes("@gjc-profile")) ?? profile.failures[0];
|
|
283
|
+
(context.diagnosticWriter ?? process.stderr.write.bind(process.stderr))(
|
|
284
|
+
formatTmuxLaunchDiagnostic("profile tagging failed", failure?.stderr),
|
|
285
|
+
);
|
|
286
|
+
return true;
|
|
268
287
|
}
|
|
269
288
|
}
|
|
270
289
|
if (created.exitCode !== 0) return false;
|
|
271
290
|
const attached = spawnSync(plan.tmuxCommand, ["attach-session", "-t", plan.sessionName], options);
|
|
272
|
-
|
|
291
|
+
if (attached.exitCode === 0) return true;
|
|
292
|
+
(context.diagnosticWriter ?? process.stderr.write.bind(process.stderr))(
|
|
293
|
+
formatTmuxLaunchDiagnostic("attach failed", attached.stderr),
|
|
294
|
+
);
|
|
295
|
+
return true;
|
|
273
296
|
}
|
|
@@ -1673,9 +1673,10 @@ function buildWorkerCommand(config: GjcTeamConfig, worker: GjcTeamWorker): strin
|
|
|
1673
1673
|
`You are ${worker.id} in gjc team ${config.team_name}.`,
|
|
1674
1674
|
`Team state root: ${config.state_root}.`,
|
|
1675
1675
|
workspace,
|
|
1676
|
-
`
|
|
1676
|
+
`Team brief (context only): ${config.task}`,
|
|
1677
|
+
"Before implementation, claim your worker-owned task and treat the claimed task record as the source of truth. Do not implement directly from the broad team brief.",
|
|
1677
1678
|
`Before claiming work, send startup ACK: gjc team api worker-startup-ack --input '{"team_name":"${config.team_name}","worker_id":"${worker.id}","protocol_version":"1"}' --json.`,
|
|
1678
|
-
`Use gjc team api update-worker-status to report task-local activity, then claim-task/transition-task-status with this worker id; record completion_evidence (summary plus a passed command or verified inspection/artifact item) before completed, and do not mutate leader-owned goal state.`,
|
|
1679
|
+
`Use gjc team api update-worker-status to report task-local activity, then claim-task/transition-task-status with this worker id; keep heartbeat current during long work, record completion_evidence (summary plus a passed command or verified inspection/artifact item) before completed, and do not mutate leader-owned goal state.`,
|
|
1679
1680
|
].join("\n");
|
|
1680
1681
|
const env = [
|
|
1681
1682
|
`GJC_TEAM_WORKER=${shellQuote(`${config.team_name}/${worker.id}`)}`,
|
|
@@ -1689,7 +1690,80 @@ function buildWorkerCommand(config: GjcTeamConfig, worker: GjcTeamWorker): strin
|
|
|
1689
1690
|
];
|
|
1690
1691
|
return `${env.join(" ")} ${config.worker_command} ${shellQuote(prompt)}`;
|
|
1691
1692
|
}
|
|
1693
|
+
interface GjcTeamInitialLane {
|
|
1694
|
+
label: string;
|
|
1695
|
+
title: string;
|
|
1696
|
+
body: string;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
function normalizeLaneId(label: string): string {
|
|
1700
|
+
return `lane-${sanitizeName(label).toLowerCase() || stableHash(label).slice(0, 8)}`;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
function parseExplicitTeamLanes(task: string): GjcTeamInitialLane[] {
|
|
1704
|
+
const lines = task.split(/\r?\n/);
|
|
1705
|
+
const lanes: GjcTeamInitialLane[] = [];
|
|
1706
|
+
let current: { label: string; title: string; body: string[] } | null = null;
|
|
1707
|
+
const laneHeading = /^#{2,6}\s+Lane\s+([A-Za-z0-9]+)\s*(?:[—–-]\s*(.+))?\s*$/;
|
|
1708
|
+
const boundaryHeading = /^#{1,6}\s+(?:Integration Owner|Verification Plan|ADR|Approval State)\b/i;
|
|
1709
|
+
|
|
1710
|
+
for (const line of lines) {
|
|
1711
|
+
const match = line.match(laneHeading);
|
|
1712
|
+
if (match) {
|
|
1713
|
+
if (current) lanes.push({ ...current, body: current.body.join("\n").trim() });
|
|
1714
|
+
current = {
|
|
1715
|
+
label: match[1] ?? `${lanes.length + 1}`,
|
|
1716
|
+
title: (match[2] ?? `Lane ${match[1] ?? lanes.length + 1}`).trim(),
|
|
1717
|
+
body: [],
|
|
1718
|
+
};
|
|
1719
|
+
continue;
|
|
1720
|
+
}
|
|
1721
|
+
if (current && boundaryHeading.test(line)) {
|
|
1722
|
+
lanes.push({ ...current, body: current.body.join("\n").trim() });
|
|
1723
|
+
current = null;
|
|
1724
|
+
continue;
|
|
1725
|
+
}
|
|
1726
|
+
if (current) current.body.push(line);
|
|
1727
|
+
}
|
|
1728
|
+
if (current) lanes.push({ ...current, body: current.body.join("\n").trim() });
|
|
1729
|
+
return lanes.filter(lane => lane.body.length > 0 || lane.title.length > 0);
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
function hasAmbiguousLaneSplitIntent(task: string): boolean {
|
|
1733
|
+
return (
|
|
1734
|
+
/\bsplit\s+lanes?\s*:/i.test(task) || /\blanes?\s*:\s*[A-Z]\b/i.test(task) || /\bLane\s+[A-Z]\s*[—–-]/.test(task)
|
|
1735
|
+
);
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1692
1738
|
function buildInitialTasks(task: string, workers: GjcTeamWorker[]): GjcTeamTask[] {
|
|
1739
|
+
const lanes = parseExplicitTeamLanes(task);
|
|
1740
|
+
if (lanes.length > 0)
|
|
1741
|
+
return lanes.map((lane, index) => {
|
|
1742
|
+
const worker = workers[index % workers.length];
|
|
1743
|
+
if (!worker) throw new Error("team_lane_requires_worker");
|
|
1744
|
+
const laneTitle = `Lane ${lane.label} — ${lane.title}`;
|
|
1745
|
+
const objective = [`${laneTitle}`, lane.body].filter(part => part.trim().length > 0).join("\n\n");
|
|
1746
|
+
return {
|
|
1747
|
+
id: `task-${index + 1}`,
|
|
1748
|
+
subject: laneTitle,
|
|
1749
|
+
description: objective,
|
|
1750
|
+
title: laneTitle,
|
|
1751
|
+
objective,
|
|
1752
|
+
status: "pending",
|
|
1753
|
+
owner: worker.id,
|
|
1754
|
+
lane: normalizeLaneId(lane.label),
|
|
1755
|
+
required_role: worker.role,
|
|
1756
|
+
version: 1,
|
|
1757
|
+
created_at: now(),
|
|
1758
|
+
updated_at: now(),
|
|
1759
|
+
};
|
|
1760
|
+
});
|
|
1761
|
+
|
|
1762
|
+
if (workers.length > 1 && hasAmbiguousLaneSplitIntent(task))
|
|
1763
|
+
throw new Error(
|
|
1764
|
+
"ambiguous_team_lane_split: multi-worker team launch mentions lanes but does not provide explicit markdown lane sections such as `### Lane A — Title`",
|
|
1765
|
+
);
|
|
1766
|
+
|
|
1693
1767
|
return workers.map(worker => ({
|
|
1694
1768
|
id: `task-${worker.index}`,
|
|
1695
1769
|
subject: `Execute team brief (${worker.id})`,
|
|
@@ -2456,6 +2530,7 @@ export async function startGjcTeam(options: GjcTeamStartOptions): Promise<GjcTea
|
|
|
2456
2530
|
? { sessionName: "dry-run", windowIndex: "0", leaderPaneId: "%dry-run-leader", target: "dry-run:0" }
|
|
2457
2531
|
: readCurrentTmuxLeaderContext(tmuxCommand, env);
|
|
2458
2532
|
const initialWorkers = buildWorkers(options.workerCount, options.agentType, stateRoot);
|
|
2533
|
+
const initialTasks = buildInitialTasks(options.task, initialWorkers);
|
|
2459
2534
|
const workers: GjcTeamWorker[] = [];
|
|
2460
2535
|
try {
|
|
2461
2536
|
for (const worker of initialWorkers)
|
|
@@ -2509,7 +2584,7 @@ export async function startGjcTeam(options: GjcTeamStartOptions): Promise<GjcTea
|
|
|
2509
2584
|
updated_at: createdAt,
|
|
2510
2585
|
});
|
|
2511
2586
|
await writePhase(dir, "starting");
|
|
2512
|
-
for (const task of
|
|
2587
|
+
for (const task of initialTasks) await writeTask(dir, task);
|
|
2513
2588
|
await appendEvent(dir, {
|
|
2514
2589
|
type: "team_started",
|
|
2515
2590
|
message: options.dryRun
|
|
@@ -3,6 +3,7 @@ import { DEFAULT_ULTRAGOAL_OBJECTIVE } from "./goal-mode-request";
|
|
|
3
3
|
import {
|
|
4
4
|
computeUltragoalPlanGeneration,
|
|
5
5
|
getUltragoalPaths,
|
|
6
|
+
getUltragoalRunCompletionState,
|
|
6
7
|
hashStructuredValue,
|
|
7
8
|
readUltragoalLedger,
|
|
8
9
|
readUltragoalPlan,
|
|
@@ -246,7 +247,8 @@ export async function readUltragoalVerificationState(input: {
|
|
|
246
247
|
message: "Ultragoal has recorded review blockers; complete blocker work and rerun verification.",
|
|
247
248
|
};
|
|
248
249
|
}
|
|
249
|
-
|
|
250
|
+
const runState = getUltragoalRunCompletionState(plan);
|
|
251
|
+
if (runState.incompleteGoals.some(goal => goal.status === "blocked" || goal.status === "failed")) {
|
|
250
252
|
return {
|
|
251
253
|
state: "active_dirty_quality_gate",
|
|
252
254
|
message: "Ultragoal has blocked or failed goals; record blockers or rerun verification.",
|
|
@@ -259,7 +261,21 @@ export async function readUltragoalVerificationState(input: {
|
|
|
259
261
|
message: "Ultragoal aggregate completion requires a fresh final aggregate receipt.",
|
|
260
262
|
};
|
|
261
263
|
}
|
|
262
|
-
|
|
264
|
+
const receiptDiagnostic = validateCompletionReceipt({
|
|
265
|
+
plan,
|
|
266
|
+
ledger,
|
|
267
|
+
goal: receiptTarget.goal,
|
|
268
|
+
receiptKind: receiptTarget.receiptKind,
|
|
269
|
+
});
|
|
270
|
+
if (receiptDiagnostic.state !== "active_verified_complete") return receiptDiagnostic;
|
|
271
|
+
if (runState.incompleteGoals.length > 0) {
|
|
272
|
+
return {
|
|
273
|
+
state: "active_missing_final_receipt",
|
|
274
|
+
message: `Ultragoal still has incomplete required goals: ${runState.incompleteGoals.map(goal => goal.id).join(", ")}. Run \`gjc ultragoal complete-goals\` to continue.`,
|
|
275
|
+
goalId: receiptTarget.goal.id,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
return receiptDiagnostic;
|
|
263
279
|
}
|
|
264
280
|
|
|
265
281
|
export async function assertCanCompleteCurrentGoal(input: {
|