feat(autoresearch)!: replace the team workflow with a first-class autoresearch skill - #4430
feat(autoresearch)!: replace the team workflow with a first-class autoresearch skill#4430Yeachan-Heo wants to merge 29 commits into
Conversation
4dea445 to
0a08258
Compare
|
The reported evidence-producer failure belonged to superseded head A dedicated Kimi adversarial review lane is active with accepted turn — |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4dea4456c4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| ### Phase 2 — iterate | ||
|
|
||
| Iterate experiments on an `autoresearch/*` branch with baseline/keep/discard discipline. Log every run: `keep` when the primary metric improves, `discard` when it regresses or stays flat, `crash` when the run fails, `checks_failed` when validation fails. A `keep` commits the run only on the dedicated branch; `discard`/`crash`/`checks_failed` revert it. Flag suspect runs (reward-hacked, invalid, unjustified) so they are excluded from baseline and best-metric math. When the worktree is too dirty to branch, the loop degrades to off-branch mode: keeps stay in the worktree, and discards revert only run-modified paths so pre-existing user dirt survives. |
There was a problem hiding this comment.
Keep autoresearch from committing implementation changes
For experiments that require changing project code, this phase explicitly commits improved edits—or leaves them in the user's worktree when branch isolation is unavailable—so invoking the research-only workflow performs implementation without approval. Remove code-changing iteration from this skill or route it through the normal approval-gated execution path.
AGENTS.md reference: AGENTS.md:L100-L106
Useful? React with 👍 / 👎.
| : renderHandoffIntakeText(receipt), | ||
| }; | ||
| } | ||
| const goal = extractPositionalGoal(args); |
There was a problem hiding this comment.
Dispatch autoresearch verbs before treating arguments as a goal
Every non---spec invocation is routed through cold intake, so the documented gjc autoresearch read --json returns a clarification receipt with goal read, while gjc autoresearch clear does not clear anything. Cold intake also has no reachable write verb after clarification, leaving it unable to create the mission required by the Python tool; dispatch read, write, and clear explicitly before parsing positional goal text.
Useful? React with 👍 / 👎.
| const receipt = await autoresearchRead(input.cwd, input.getSessionId()); | ||
| if (!receipt.exists || !receipt.mission) return null; | ||
| const { writer } = await openMissionNotebook(input.cwd, receipt.mission); | ||
| return { | ||
| missionId: receipt.mission.slug, | ||
| artifactsDir: missionArtifactsDir(input.cwd, receipt.mission), |
There was a problem hiding this comment.
Thread the live session ID into notebook path resolution
In a normal direct AgentSession launch, getSessionId() supplies the mission reader with the correct session while GJC_SESSION_ID is unset; openMissionNotebook and missionArtifactsDir then discard that ID and eventually call resolveGjcSessionForWrite using only the environment, which throws missing_for_write. Consequently the discoverable Python tool reports unreadable mission state instead of executing for ordinary sessions; pass the resolved session ID through the notebook/artifact helpers.
Useful? React with 👍 / 👎.
| const [{ autoresearchRead }, { openMissionNotebook, missionArtifactsDir }] = await Promise.all([ | ||
| import("../gjc-runtime/autoresearch-runtime"), | ||
| import("./session"), | ||
| ]); |
There was a problem hiding this comment.
Move mission resolver dependencies to top-level imports
The live Python tool performs two inline import() calls on every mission-context resolution, contrary to the repository's top-level-import-only convention. Move these dependencies to top-level imports, extracting the mission reader into a cycle-neutral module if necessary.
AGENTS.md reference: AGENTS.md:L111-L114
Useful? React with 👍 / 👎.
| beforeAll(() => { | ||
| previousGjcSessionId = process.env.GJC_SESSION_ID; | ||
| process.env.GJC_SESSION_ID = TEST_SESSION_ID; | ||
| }); |
There was a problem hiding this comment.
Scope the session environment mutation to individual tests
This suite pins process.env.GJC_SESSION_ID for its entire lifetime, so sibling tests sharing the Bun process can resolve state against test-session instead of exercising missing/explicit session behavior; this can both make results order-dependent and mask the notebook session-propagation failure. Pass session IDs explicitly or use a per-test scoped mutation with immediate cleanup.
AGENTS.md reference: AGENTS.md:L154-L158
Useful? React with 👍 / 👎.
3fd57b0 to
6529879
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6529879215
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| 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. | ||
| 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 execution is already authorized by the resolved handoff admission, then persist the ADR/final plan via `gjc ralplan --write --stage final --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json`. Read the successful receipt's `auto_handoff` object; its ledger-backed `effectiveTarget` is runtime-owned and is the only automatic-routing decision; 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. | ||
| 8. **Final admission and approval gate:** Reconciliation must first reach the successful final receipt from step 7. If that receipt has `auto_handoff.degradationReason: "planning_stuck"`, it is terminal: retain the `pending approval` artifact and **never dispatch**, including for an explicitly named execution skill; do not issue an approval `ask`. Otherwise, if its runtime-owned `auto_handoff.effectiveTarget` is `ultragoal` or `team`, that valid non-off receipt is explicit operator admission for same-turn execution through that target; proceed to step 9 without an `ask`. If it is `off`, including ordinary `off` or a runtime degradation such as `team_unavailable:<reason>`, preserve the ordinary approval flow: 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, 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: | ||
| 8. **Final admission and approval gate:** Reconciliation must first reach the successful final receipt from step 7. If that receipt has `auto_handoff.degradationReason: "planning_stuck"`, it is terminal: retain the `pending approval` artifact and **never dispatch**, including for an explicitly named execution skill; do not issue an approval `ask`. Otherwise, if its runtime-owned `auto_handoff.effectiveTarget` is `ultragoal`, that valid non-off receipt is explicit operator admission for same-turn execution through that target; proceed to step 9 without an `ask`. If it is `off`, including ordinary `off`, preserve the ordinary approval flow: if the user already explicitly named an execution skill in the current turn or via the structured approval UI (`ultragoal`, `/skill:ultragoal`, `gjc ultragoal`, or "Approve execution via ultragoal"), that is execution approval — skip the re-ask and proceed to step 9 with that skill. Otherwise, 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: |
There was a problem hiding this comment.
Handle the autoresearch admission target
When gjc.ralplan.autoHandoff is configured as autoresearch, the runtime persists effectiveTarget: "autoresearch", but this gate handles only ultragoal and off; step 9 likewise invokes only ultragoal. That leaves an admitted plan with no defined continuation, so the documented automatic research handoff stalls instead of invoking /skill:autoresearch. Add an explicit autoresearch branch that performs the research-only handoff.
AGENTS.md reference: AGENTS.md:L100-L106
Useful? React with 👍 / 👎.
| .replace(/[^A-Za-z0-9_-]+/g, "-") | ||
| .replace(/^-+|-+$/g, "") | ||
| .slice(0, 128); | ||
| return isValidRlmSessionId(sanitized) ? sanitized : generateRlmSessionId(); |
There was a problem hiding this comment.
Make the fallback notebook identifier stable
For a valid cold-intake slug that sanitizes to nothing, such as a Unicode-only slug, this generates a new ID on every call. openMissionNotebook even calls this helper separately for the write path and the resume read, and later Python calls and report generation get still more IDs, so cells are never resumed and the final report can point at an empty notebook. Reject such slugs or derive one deterministic fallback from the persisted mission.
Useful? React with 👍 / 👎.
| const ledgerEvent = await appendAutoresearchLedger( | ||
| cwd, | ||
| { event: "kernel_cleared", slug: existing?.slug ?? "" }, | ||
| resolvedSessionId, |
There was a problem hiding this comment.
Actually dispose the kernel before recording it as cleared
Even after the separately reported CLI verb-dispatch problem is repaired, autoresearchClear only removes mission.json and appends kernel_cleared; it never calls disposeKernelSessionsByOwner for autoresearch:<slug>. If another mission with the same slug is created in the same agent session, the Python tool reconnects to the old live kernel and exposes variables/imports from the supposedly cleared mission. Dispose the existing mission owner before emitting this ledger event.
Useful? React with 👍 / 👎.
| if (!mission) throw new AutoresearchCommandError(2, "autoresearch report requires an active mission"); | ||
| const { writer, paths } = await openMissionNotebook(cwd, mission); | ||
| const ledger = await readAutoresearchLedger(cwd, sessionId); | ||
| const verdict = extractVerdictFromLedger(ledger); |
There was a problem hiding this comment.
Select the verdict for the current mission
The session ledger is intentionally retained across clear and subsequent mission creation, but report generation takes the latest verdict in the entire ledger without checking its slug. If mission A issues a verdict, is cleared, and mission B is started without a verdict yet, mission B's report embeds mission A's verdict and evidence. Filter ledger events by the active mission slug before selecting the latest receipt.
Useful? React with 👍 / 👎.
CI status: all remaining failures are pre-existing on
|
| Failing check | Cause | On clean dev? |
|---|---|---|
check:@gajae-code/coding-agent |
lint errors in src/sdk/bus/index.ts, src/sdk/bus/telegram-daemon.ts, src/sdk/host/session-runtime.ts, src/session/session-manager.ts, src/config/settings.ts — none touched here |
❌ fails |
root-check |
same, via check:tools → ci:check:full |
❌ fails |
test:…/agent-session-message-pipeline.test.ts |
forwards stop reasons and reasoning summaries to extension handlers |
❌ fails |
test:…:shard-1-of-8 |
contains the two dev tests above (sdk-memory-startup, agent-session-message-pipeline) |
❌ fails |
evidence producer |
fails closed because required shards above failed | cascade |
Affected path validation |
aggregate of the above | cascade |
Deliberately not fixed here: they are unrelated to retiring team/rlm and belong in their own PR, per the repo's one-logical-change-per-commit rule. Bundling SDK/session lint and two SDK test regressions into a 21-commit breaking-change PR would make this much harder to review.
What this PR's own surface does
Everything owned by this change is green:
Local public surfaces✅gjc-state-gates / static,/ read,/ integrity✅- all
test:packages/coding-agent/test/autoresearch/*shards ✅ test:…/default-gjc-definitions.test.ts,cli-command-surface,gjc-skill-state-hooks,workflow-hud-summary,skill-active-state*,workflow-state-command,generate-gjc-sdk-skills✅packages/tuiautocomplete + editor-autocomplete-actions ✅ts-buildforcoding-agentandstats✅
Two of my own breakages that CI legitimately caught
Both are fixed in this branch, and both are worth noting because a green local run hid them:
-
customizeCLI command —devregistered a new command while this branch was in review.cli-command-surfaceasserts the registered list in registration order, so it failed on both membership and position. Fixed by inserting it at its real position. -
Ambient
GJC_SESSION_ID—test/autoresearch/session.test.tsandtest/autoresearch/python-tool-builtin.test.tsread the session id from the environment. They passed locally only because they were authored inside a live GJC session where that variable is set; on a clean runner the mission resolver raisedSessionResolutionError, which the python tool's fail-closed catch surfaced as an error result. Both now pin the id inbeforeAlland restore it inafterAll, matching the existing pattern inruns/report/capabilities. Reproduced before the fix and verified after withenv -u GJC_SESSION_ID.
The second one is the more interesting failure: the tool behaved correctly — it failed closed rather than starting an ad-hoc kernel — and the test was what was wrong.
6529879 to
a58dd4b
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a58dd4b0a0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const ownerId = autoresearchKernelOwnerId(missionContext.missionId); | ||
| seenOwnerIds.add(ownerId); |
There was a problem hiding this comment.
Namespace mission kernels by live session
When two live AgentSessions in the same process run missions with the same slug, this derives the identical autoresearch:<slug> owner and passes it as the executor session ID. The executor keeps a process-global map keyed only by that ID, so the second mission reuses the first mission's Python process, exposing variables and loaded data across sessions; clearing or disposing either mission can also terminate the other's kernel. Include the GJC session identity—and ideally the project identity—in the kernel session/owner key.
Useful? React with 👍 / 👎.
| 3. **Continue research with autoresearch (research continuation, not execution)** | ||
| - Description: "Feed the crystallized spec into an autoresearch mission to deepen research grounding before any implementation planning. This is not an execution path and implements nothing." | ||
| - Action: Invoke `/skill:autoresearch` with the spec file path as context only after the user explicitly selects this option. The crystallized spec seeds the research mission, which then feeds the productification interview before any ralplan/ultragoal planning, per the chain deep-interview → autoresearch → productification interview → ralplan/ultragoal. Do not treat this as an execution handoff: no implementation happens here. |
There was a problem hiding this comment.
Make deep-interview emit a valid autoresearch handoff
When the user selects this new continuation, the instruction passes the spec path merely "as context," although autoresearch recognizes a handoff only through --spec; even if the agent translates it to --spec, the canonical deep-interview spec template never emits the required autoresearch-mode: declaration, so parseAutoresearchSpec rejects it. Collect and persist the mode in the spec and invoke /skill:autoresearch --spec <path> so the advertised deep-interview → autoresearch flow can actually start.
AGENTS.md reference: AGENTS.md:L107-L107
Useful? React with 👍 / 👎.
| | `ralplan` | Builds and critiques the implementation plan before code changes. | | ||
| | `ultragoal` | Tracks goals through execution, revision, verification, and evidence. | | ||
| | `team` | Coordinates tmux-backed workers when parallelism is worth it. | | ||
| | `autoresearch` | Runs goal-directed research missions and ends on a structured verdict. | |
There was a problem hiding this comment.
Sync localized READMEs with the removed command surface
The English README now advertises the replacement surface, but README.ja.md, README.ko.md, and README.zh-CN.md still list team as the fourth default workflow and direct users to the removed gjc rlm command and gjc_delegate_team MCP tool. Users following any localized README therefore receive nonexistent-command/tool failures; update those copies alongside this surface replacement.
AGENTS.md reference: AGENTS.md:L102-L106
Useful? React with 👍 / 👎.
a58dd4b to
a13dd22
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a13dd22452
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| export async function autoresearchRunsStore(cwd: string, sessionId?: string | null): Promise<AutoresearchRunsStore> { | ||
| const store = await AutoresearchRunsStore.open(cwd, sessionId); | ||
| if (store.config === null) { |
There was a problem hiding this comment.
Namespace run records by active mission
When mission A is cleared and mission B is created in the same GJC session, AutoresearchRunsStore.open reopens the same session-wide runs.jsonl and experiment.json; this branch only seeds B when no config exists and never checks the stored config's name against B's slug. As a result, B's dashboard and iterate prompt can use A's goal, metrics, and logged runs, corrupting the new mission's experimental baseline. Key run storage by mission slug or reset it as part of mission replacement.
Useful? React with 👍 / 👎.
| const h1 = lines.find(line => /^#\s+\S/.test(line.trim())); | ||
| const objective = | ||
| (h1 ?? lines.find(line => line.trim() !== ""))?.trim().replace(/^#\s+/, "") ?? path.basename(specPath); |
There was a problem hiding this comment.
Read the canonical Goal section for handoff objectives
For a spec emitted by the bundled deep-interview template, the H1 is # Deep Interview Spec: {title} while the crystallized objective is under ## Goal; this parser always selects the H1, so the autoresearch mission and its experiment prompts receive the wrapper title instead of the detailed goal. Parse the canonical Goal section first and add a fixture using the actual bundled spec format.
AGENTS.md reference: AGENTS.md:L107-L107
Useful? React with 👍 / 👎.
| import { BUNDLED_GJC_SKILL_CATALOG, type BundledGjcSkillCatalogEntry } from "./gjc-skills.generated"; | ||
|
|
||
| export const DEFAULT_GJC_DEFINITION_NAMES = ["deep-interview", "ralplan", "team", "ultragoal"] as const; | ||
| export const DEFAULT_GJC_DEFINITION_NAMES = ["autoresearch", "deep-interview", "ralplan", "ultragoal"] as const; |
There was a problem hiding this comment.
Update the discovery prompt with the canonical workflow set
After replacing team here, the live packages/coding-agent/src/prompts/tools/skill-discovery.md instruction and its generated tool-catalog description still tell the model that team is bundled and omit autoresearch. A session using skill discovery can therefore attempt the removed workflow despite the runtime catalog exposing the opposite set; update the source prompt and regenerate the catalog with this canonical-list change.
AGENTS.md reference: AGENTS.md:L102-L102
Useful? React with 👍 / 👎.
CI update — 84 pass / 5 fail, all 5 inherited from
|
| Failing check | Verification | Result |
|---|---|---|
test:…/config/settings-workflow-migration.test.ts |
git checkout origin/dev -- <that test> <src/config/settings-schema.ts> then run |
78 pass / 43 fail — byte-identical to this branch's 78/43, so the suite is broken on dev independent of my change. My only edit to it swaps one autoHandoff fixture value from "team" to "ultragoal", since team no longer validates. The failures are all maxIterations Expected: 9 / Received: 7 on backup/abort paths, unrelated to autoHandoff. |
test:…/agent-session-message-pipeline.test.ts |
run at this HEAD | fails on forwards stop reasons and reasoning summaries to extension handlers; file untouched by this branch |
test:…:shard-1-of-8 |
dev's own dev-ci run (31671259990) |
this shard is failure on dev itself — it contains the two tests above |
evidence producer |
— | fails closed because the shards above failed |
Affected path validation |
— | aggregate of the above |
Note on why settings-workflow-migration appears here but not on dev's runs: it enters the affected set only because this PR touches src/config/settings-schema.ts (one enum value for autoHandoff). dev doesn't touch that file, so its own path selection never schedules the suite — which is how a broken suite stayed invisible.
This PR's own surface
Green, including everything added or changed here:
- all
test/autoresearch/*shards, the mission runtime,agent-session-signal-teardown default-gjc-definitions,cli-command-surface,gjc-skill-state-hooks,workflow-*,skill-active-state*computer.enforcement+computer.redteam,descriptorsgjc-state-gates(static / read / integrity / runtime),Local public surfacespackages/tuiautocomplete + editor-autocomplete-actionsts-buildforcoding-agentandstats
Locally at this HEAD: 209 tests pass / 0 fail across the autoresearch suites, mission runtime, signal-teardown, default-gjc-definitions, cli-command-surface, the descriptor registry and both computer suites; check:types and check:tools both pass, with every remaining biome warning in a dev-only file.
Latest commit — documentation and runtime guidance
a13dd22452 closes three gaps where the shipped behavior had no matching guidance:
src/prompts/tools/python.md(new) — the tool now imports its model-facing description with{ type: "text" }instead of an inline string. Every other builtin already keeps its prompt there andAGENTS.mdforbids inline prompts; this tool was the lone exception.docs/tools/python.md(new) — matches the sibling per-tool docs: discoverable/defaultInactiveregistration and why activation must pass the full merged tool list,execute/clearwith no separate teardown tool, per-call mission resolution, and the fail-closed contract that a missing or corrupt mission never degrades to an ad-hoc kernel.docs/python-repl.md— new Kernel ownership section separating session-owned kernels (eval) from explicitly-owned ones (autoresearch:<mission-id>), and recording that signal exit drains both tool-cleanup registries. That last detail is the Ctrl-C orphan bug: the SDK puts a tool'sregisterSessionCleanupin the transition registry, so draining only the session registry left the kernel running while graceful dispose looked correct.docs/tools/eval.md— cross-reference so the two Python-running tools aren't confused.
Every claim in those docs was read back out of the shipped code, and the regenerated tool catalog and docs index both carry the new description.
a13dd22 to
8727e0f
Compare
Fresh terminal evidence — rebased exact head
Please perform a fresh adversarial review against this exact head/base and post a signed terminal — |
|
@codex review Review target: head — |
Signed terminal verdict: CLOSE — replacement is not mergeable
Fresh read-only adversarial audit found the intended Team → Autoresearch replacement incomplete and internally contradictory:
The preserved prior-worktree generated catalog delta is unrelated (task-agent wording only) and was not incorporated. Local focused proof did pass autoresearch/unit, pipeline, CLI/help, computer-safety, generation, and binary-build checks; that proof is insufficient to override the missing/contradictory public lifecycle. The migration and pipeline CI failures were independently attributed to current-dev baseline/environmental defects, but the blocking findings above are PR-owned. Repair requires a newly scoped, coherent Autoresearch lifecycle and policy design rather than further patching this 18-commit replacement branch. — |
|
Closing against exact head — |
Terminal ownership resumed on authoritative immutable refs
Reopened strictly for exact-head verification and a fresh terminal review. The PR will be merged only on green CI plus a clean MERGE_READY verdict; otherwise it will be signed-closed without OPEN limbo. — |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8727e0f854
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| await git.reset(input.cwd, { hard: true, target: "HEAD" }); | ||
| await git.clean(input.cwd); |
There was a problem hiding this comment.
Preserve pre-run changes when discarding on a mission branch
When a mission is resumed while already on an autoresearch/* branch, ensureAutoresearchBranch accepts the branch before checking whether it contains dirty files, but this discard path ignores preRunDirtyPaths and destroys the whole working tree. The local git reset -h states that --hard resets the index and working tree, and the git.clean wrapper invokes git clean -fd, forcibly removing untracked files and directories, so unrelated user work created before the run is lost. Apply the same run-modified-path filtering used by the off-branch path, or reject a dirty mission branch before starting.
AGENTS.md reference: AGENTS.md:L108-L108
Useful? React with 👍 / 👎.
| cwd, | ||
| skill: "autoresearch", | ||
| active: true, | ||
| phase: "mission", |
There was a problem hiding this comment.
Persist a recognized active phase for autoresearch missions
When a --spec mission reaches context compaction, this records phase mission, which is absent from the autoresearch manifest (intake, research, verdict, etc.). isWorkflowContinuationInert deliberately treats unknown phases as inert, and AgentSession.#hasUnfinishedWork consequently does not schedule continuation for this otherwise-active mission, so research can stop after compaction until the user intervenes. Persist the manifest's active research phase instead.
Useful? React with 👍 / 👎.
8727e0f to
e14ce82
Compare
Exact-head repair and baseline evidence
This head repairs the PR-owned Autoresearch blockers: explicit public Exact local proof at this head:
Current exact-head Dev CI is Baseline evidence for the superseded CI red jobs: a self-contained — |
|
@codex review Fresh adversarial review requested for exact head The prior lifecycle/closure blockers were repaired and evidence is in the preceding signed comment. Review only this immutable pair; do not rely on verdicts for superseded heads. — |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e14ce829e8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const branch = await ensureAutoresearchBranch(cwd, mission.objective).then(result => | ||
| result.ok ? result.branchName : null, | ||
| ); |
There was a problem hiding this comment.
Avoid switching branches while rendering research state
When a clean Git worktree has an active mission but no persisted experiment config, merely opening the dashboard or rendering the iteration prompt reaches this call, and ensureAutoresearchBranch executes a new-branch checkout. This silently changes the user's current branch during an otherwise read-only operation, despite the revised workflow promising that autoresearch does not create branches; seed the in-memory config without mutating Git state.
AGENTS.md reference: AGENTS.md:L108-L108
Useful? React with 👍 / 👎.
| } | ||
| if (verb === "report") { | ||
| assertOnlyAutoresearchFlags(args.slice(1), ["--summary", "--json"]); | ||
| const reportPath = await autoresearchMissionReport(cwd, flagValue(args, "--summary")); |
There was a problem hiding this comment.
Return the actual report path from the report verb
autoresearchMissionReport returns the synthesized Markdown report text, not a pathname, so every gjc autoresearch report invocation assigns the full report body to reportPath and emits it under report_path. Human output becomes a multiline pseudo-path and JSON automation cannot locate the report file; return paths.reportPath from this command or return content and path separately.
Useful? React with 👍 / 👎.
| await appendAutoresearchLedger( | ||
| input.cwd, | ||
| { | ||
| event: "verdict_issued", | ||
| ...(input.slug?.trim() ? { slug: input.slug.trim() } : {}), |
There was a problem hiding this comment.
Move issued verdicts into the verdict phase
After a mission issues its verdict, this only appends the ledger event and leaves the active workflow entry in research. If context compaction occurs before the subsequent clear, AgentSession.#hasUnfinishedWork treats that phase as active and schedules another continuation, whereas the manifest explicitly makes verdict continuation-inert; reconcile the active state to verdict after recording the receipt so a finished evidence loop does not restart.
Useful? React with 👍 / 👎.
| slug: flagValue(args, "--slug"), | ||
| }; | ||
| const receipt = | ||
| verb === "critic" ? await autoresearchRecordCritic(input) : await autoresearchIssueVerdict(input); |
There was a problem hiding this comment.
Reject lifecycle writes without an active mission
When a delayed or mistyped log-run, critic, or verdict command runs before intake or after clear, these public branches invoke appenders without checking for an active mission and still return success; the resulting unscoped events remain in the session ledger and can contaminate a later mission's history and report. Load the current mission first, reject when absent, and derive the event slug from that mission rather than accepting an optional arbitrary slug.
Useful? React with 👍 / 👎.
Exact-head terminal repair evidence
The fresh terminal review blockers are repaired:
Proof at this exact head: Exact-head Dev CI runs — |
cc08a53 to
a66c1e1
Compare
Deleting only the team command would leave a public delegation path that starts a session and prompts /skill:team for a skill that is about to stop existing. The coordinator contract, the workflow unions that persist Codex handoffs, and the generated plugin bundle all have to drop team together or the protocol advertises a dead workflow. Lore-id: c3f2e9b7 Constraint: generic coordinator/session/tmux plumbing stays -- only the team-specific delegate contract is removed Constraint: the gjc_delegate_plan / gjc_delegate_execute round-trip pair must keep passing; that pair is the falsifiable AC-8b evidence Rejected: remap gjc_delegate_team onto ultragoal | silently changes what a caller asked for, and the architect review called this out explicitly Rejected: keep the tool and reject at call time | a shim by another name Confidence: high Scope-risk: moderate Reversibility: easy Directive: the delegate self-check count is load-bearing -- generate-gjc-plugins and verify-gjc-plugins both assert the exact delegate set, so they move with the contract Tested: coordinator-mcp-server 92/92 (incl. the plan/execute lifecycle round-trip), coordinator-codex-bridge-redteam 7/7, setup-cli 18/18, generate-gjc-plugins --self-test, verify-gjc-plugins 16/16 gates, generate-gjc-plugins --check in sync; tsc and biome clean Not-tested: verify-g002-gates -- pre-existing failure at base 4c16e70
The team surface is being replaced by autoresearch. Everything except the canonical slot itself can go now: the command, the eight team-* runtime modules, and every consumer that reached into them. Team stays canonical and stays in the skills directory through this commit so all four-skill gates remain green -- the slot swap is a separate atomic change. Removing team-runtime orphaned WorkerIntegrationRequestScheduler, whose only production caller was the team worker-integration flow but which is independently covered by g004-redteam and agent-session-abort-timeout. Rather than delete a red-team gate or leave a no-op fallback, the class stays as a generic primitive and worker integration became an injected seam: no scheduler is constructed unless a host supplies the request. Lore-id: c4d1a8f3 Constraint: generic tmux-*.ts plumbing is untouched -- only team-owned modules and their consumers are removed Constraint: autoHandoff lands the interim off|ultragoal enum here; autoresearch is added in the slot-swap commit, since admission has to dispatch a skill that actually resolves Rejected: delete WorkerIntegrationRequestScheduler and its tests | weakens a G004 red-team gate to make a refactor pass Rejected: keep the scheduler with a no-op default request | a fake fallback masquerading as a live path Confidence: high Scope-risk: wide Reversibility: migration-needed Directive: a stale `gjc.ralplan.autoHandoff: team` in a user config now exits 2 by design -- no coercion shim Directive: shared red-team tests kept their harness_leases and file_locks assertions at full strength; only the team adapter rows were removed Tested: 122 targeted tests green across default-gjc-definitions, cli-command-surface, cli-help-load-order, completion-cli, settings-workflow-migration, agent-session-message-pipeline, agent-session-state-aware-compaction, resource-gc-redteam, gc-redteam and g004-redteam; plus gc-runtime, resource-gc, ralplan-runtime, agent-session-abort-timeout and state-writer-drift. check-visible-definitions, verify-gjc-skill-docs, verify-gjc-plugins, rebrand-inventory --strict, tsc and biome all pass Not-tested: verify-g002-gates -- pre-existing failure at base 4c16e70
disposeChildSubprocesses reaped only #evalKernelOwnerId, so a resource registered through registerToolSessionCleanup under a different owner id survived Ctrl-C. Until now nothing hit that gap in production because `gjc rlm` reaped its own kernel in a command-level finally -- which C1 deleted. The autoresearch mission kernel uses a distinct owner id by design, so without this the first signal exit orphans a Python subprocess. The drain reuses the existing #runToolSessionCleanups, which clears the set as it runs, so graceful dispose and signal exit cannot double-free, and it sits inside the existing Promise.race budget rather than adding a second timer. Lore-id: c5e3b7d2 Constraint: the mission owner id must stay distinct from the eval owner (spec f33), so aliasing the two was not an option Rejected: a watchdog or orphan-reaper daemon | explicit non-goal, and a bounded drain on the path that already exists is enough Confidence: high Scope-risk: narrow Reversibility: easy Tested: agent-session-signal-teardown 8/8. The four new cases were proven fail-today by temporarily reverting the drain: all four failed, including the hang case which blew past the time box at 5059ms, then passed again once restored
First additive half of the rebuilt subsystem: a thin `gjc autoresearch`
command in the shape of `gjc ultragoal`, a runtime that owns the mission
artifact, the JSONL ledger, and verdict/critic receipts, and the
persistent Python tool the data lane runs on.
Mode is validated at the mission-write boundary and is never inferred --
a spec or invocation without an explicit web|mixed|data mode is rejected
even when a data file is sitting right there. Ledger and mission state
live under `.gjc/_session-{id}/autoresearch/`; nothing writes to the old
project-global `~/.gjc/autoresearch` store.
The python tool is registered at construction as a `defaultInactive`
ToolDefinition and activated by merging into the full active-tool list,
because `setActiveToolsByName` silently drops names it does not know and
`activateDiscoveredTools` only ever sees built-ins. Clearing the kernel
is an action on that same tool -- no second teardown tool -- and the
mission owner id stays distinct from the session eval owner.
Lore-id: c5a4f1b8
Constraint: purely additive -- no canonical list, skills directory, gate
script, or settings schema is touched; the slot swap is still ahead
Constraint: verdict `status` stays structured data; pinning a terminality
enum is deliberately deferred
Rejected: activateDiscoveredTools / loadMode "discoverable" | built-in
discovery only, would have silently no-opped
Rejected: a separate clear/teardown tool | prohibited by AC-20
Confidence: medium
Scope-risk: moderate
Reversibility: easy
Directive: renamed the leaked `autoresearch/rlm/` path segment to
`autoresearch/runs/` before it became a permanent layout
Tested: autoresearch-runtime 20/20 incl. mode-rejection, ledger ordering,
verdict/critic round-trip and both handoff-intake paths; autoresearch
python-tool suite; signal-teardown 8/8; cli-command-surface. tsc and
biome clean, with biome verified against a stashed baseline so the one
issue I introduced was found and fixed rather than absorbed
Not-tested: the eight retained capabilities and the bundled SKILL.md --
they are the remaining half of this story
Completes the additive half of the subsystem under the zero-cut mandate:
every capability the deleted extension provided comes back, ported onto
session-scoped state instead of the project-global SQLite store.
Branch isolation keeps the reference's degraded path rather than the
happy path only: a dirty worktree off an autoresearch branch warns and
continues, and in that state keep skips auto-commit while discard reverts
only run-modified paths. Flagged runs stay excluded from baseline and
best-metric math. Data context is gated to explicit data/mixed mode, so
the no-inference rule holds at the consumption end and not just at the
mission write.
Lore-id: c5c9e2a6
Constraint: still additive -- no skills directory, canonical list, gate
script, or settings schema is touched; the slot swap is the next commit
Constraint: no SQLite and no ~/.gjc/autoresearch; everything resolves
under sessionAutoresearchDir
Confidence: medium
Scope-risk: moderate
Reversibility: easy
Directive: the two-phase prompts are static .md imported with
{ type: "text" }, scrubbed of the deleted /autoresearch command and the
old global state paths
Tested: 99 tests green across the nine autoresearch suites plus the
runtime suite, covering dirty-worktree degraded mode, METRIC/ASI parsing
with malformed and interleaved lines, flagged-run exclusion, metric
direction, data context refused in web mode, notebook cell per python
call, and the verdict reaching the report. tsc and biome clean
Not-tested: end-to-end mission execution -- that needs the public skill,
which lands with the slot swap
The exact-four-skill invariant is asserted across canonical lists, the defaults catalog, four gate scripts, and their tests, so team leaves and autoresearch arrives in one commit. Every intermediate ordering fails at least one gate: deleting team/SKILL.md before the gate lists flip reports a missing bundled skill, and adding autoresearch/SKILL.md first yields five skill directories against a hard four. Also lands the reconciled autoHandoff decision: the enum becomes off|ultragoal|autoresearch so the freed slot is reachable by ralplan's automatic admission. That value could not ship earlier, because admission dispatches /skill:autoresearch and the skill only resolves now. state-schema.ts previously duplicated the canonical list as its own literal; it now imports the single source, so the four-skill set exists in exactly one place and cannot drift. BREAKING CHANGE: `gjc team`, `/skill:team`, and the team runtime are gone. A stale `gjc.ralplan.autoHandoff: team` in a user config exits 2 by design -- no coercion shim, per the no-back-compat rule. Lore-id: c6f7b2e4 Constraint: this commit cannot be split; a transient three-skill state double-edits every gate and leaves a capability gap Rejected: keep a team alias for one release | AGENTS.md forbids shims Confidence: medium Scope-risk: wide Reversibility: migration-needed Directive: generated artifacts are regenerated, never hand-edited -- plugins, sdk-skills, schemas, docs-index and the workflow manifest all re-ran and are in sync Tested: 123 tests green across default-gjc-definitions, gjc-skill-state-hooks, coordinator-codex-bridge-redteam, workflow-hud-summary and cli-command-surface, plus 125 across the TUI autocomplete/editor and input-controller suites. check-visible-definitions, verify-gjc-skill-docs, verify-gjc-plugins, verify-gjc-state-writers, rebrand-inventory --strict, tsc and biome (coding-agent + tui) all pass Not-tested: verify-g002-gates still fails its MCP quarantine check -- reproduced identically at the untouched base commit 4c16e70, so it is pre-existing branch breakage; its definition/skill checks now pass
… policy Final verification fallout from the slot swap. `AGENTS_RETENTION` in the workflow manifest had no consumer once the team manifest was replaced, which biome flagged as a hard error rather than a warning. The rest is formatter drift across files this branch touched. Lore-id: c7d5a013 Confidence: high Scope-risk: narrow Reversibility: easy Directive: formatting was applied only to files this branch changed against base 4c16e70, not repo-wide Tested: check:tools passes; 316 tests green across the autoresearch suites, default-gjc-definitions, gjc-skill-state-hooks, cli-command-surface, agent-session-signal-teardown, g004-redteam, gc-redteam and coordinator-mcp-server Not-tested: check:sdk-closure and verify-g002-gates still fail, both reproduced identically at the untouched base commit 4c16e70 (agent_session:syncEagerDelegation pending seam; MCP quarantine)
… builtin Boundary review caught that the mission python tool was defined but never reachable: the old registration seam died with RlmPreset, and the thin `gjc autoresearch` command never launches a session. The skill told the model to use a `python` tool that was not in the registry, so AC-19's activation half only ever held inside unit tests. It is now a `loadMode: "discoverable"` builtin descriptor following the `computer` precedent -- registered but inactive, activated on demand through the discovery path, which is exactly the hidden-tool activation the interview settled on. The tool resolves the active mission from session state per call and fails closed with an actionable error when none exists, rather than quietly starting a session-scoped kernel. Also clears both cohort advisories: `src/rlm/preset.ts` plus its now orphaned `rlm-research.md` prompt are deleted (the RlmPreset hook was their only consumer, so commit 1c3c83b3f's promise that the retained engine material would be consumed by the rebuild was not true for these two), and a duplicated `#acquirePowerAssertion()` call is removed. Lore-id: c7e8f4b1 Constraint: no silent fallback -- a missing mission must error, never degrade to an ad-hoc kernel Constraint: kernel owner stays autoresearch:<mission-id>, distinct from the session eval owner Rejected: register through options.customTools | no session-creation seam survives, and the thin command must not grow one Confidence: high Scope-risk: moderate Reversibility: easy Tested: 163 tests green across the autoresearch suites, the runtime suite, signal-teardown, the descriptor registry and default-gjc-definitions; new python-tool-builtin tests prove the descriptor is discoverable and inactive by default, that activation makes it callable, and that a no-mission call fails closed without starting a kernel. check:tools, check-visible-definitions, verify-gjc-skill-docs, verify-gjc-plugins and rebrand-inventory --strict all pass
The generation-1 teardown fix drained the wrong set for the consumer it was written for. The SDK binds a tool's `registerSessionCleanup` to registerToolSessionTransitionCleanup (sdk/session.ts:1925), so the autoresearch mission kernel disposer lands in #toolSessionTransitionCleanups -- while disposeChildSubprocesses drained only #toolSessionCleanups. Graceful dispose drains both, so this was invisible there: Ctrl-C still orphaned the mission kernel in production. Every suite passed anyway, because the test registered through registerToolSessionCleanup -- the set the drain already covered -- instead of the production binding. The QA red-team lane caught it by tracing the SDK binding rather than trusting the green run; the architect and cleaner lanes both missed it. Also makes a corrupt or unreadable mission fail closed the same way a missing one does. It previously threw out of resolveMissionContext before the null check, so the caller got an opaque throw instead of the actionable message. No kernel started either way, but the boundary should not leak. Lore-id: c7f2d8a5 Constraint: no ad-hoc kernel on any failure path -- missing, corrupt, and unreadable mission state all return the same actionable error Rejected: rebind registerSessionCleanup to the non-transition set | would change teardown semantics for every other tool that uses the SDK seam Confidence: high Scope-risk: narrow Reversibility: easy Directive: teardown tests must register through the production binding -- a test that wires the drain's own set proves nothing about production Tested: signal-teardown 11/11, and the three new cases proven fail-today by reverting the transition drain (all three failed, hang case blew the time box at 5052ms). 137 tests green across the autoresearch suites, the runtime suite, signal-teardown and the descriptor registry; check:tools and biome pass
The slot-swap commit claimed AC-27 but only updated system-prompt.md and AGENTS.md; the terminal critic caught that five of the seven docs the plan named still advertised retired surfaces. Removed the whole Team tmux backend section from environment-variables.md (it documented eight GJC_TEAM_* vars that no longer exist), the gjc_delegate_team references in README.md and bot-integration.md, and the /skill:team mention in analyze-me-with-gjc.md. GJC_TEAM_TMUX_COMMAND was still honored as a live fallback alias in psmux-detect.ts and described in tmux-common.ts, so the doc row was accurate and the code was the thing out of date. Removed the alias and its prose, leaving GJC_TMUX_COMMAND as the single override. Lore-id: c7a9e6d3 Constraint: only team-named surfaces go; the generic tmux plumbing they lived in stays Rejected: keep GJC_TEAM_TMUX_COMMAND as a silent alias | a compatibility shim for a command that no longer exists Confidence: high Scope-risk: narrow Reversibility: easy Tested: 226 tests green across psmux-detect, tmux-sessions, the autoresearch suites, the runtime suite, signal-teardown and default-gjc-definitions; types, check:tools, check-visible-definitions, verify-gjc-skill-docs, verify-gjc-plugins and rebrand-inventory --strict all pass. AC-27 sweep now returns zero live references to gjc team, /skill:team, gjc rlm, gjc_delegate_team or GJC_TEAM_* outside historical prompt-architect archives
The terminal critic caught that my previous sweep used too narrow a grep: it matched `gjc team` and `/skill:team` but missed bare `team` in prose and skill lists, so two docs the plan named were never touched and several others still advertised a four-skill set containing team. Fixed: codebase-overview.md (CLI command list, state categories), onboarding-packet.md (workflow loop, default skill list, state categories), gjc-plugins.md and ui-design-visual-qa.md (the exact-four skill enumerations), gjc-dogfood-skill-template.md and extragoal-skill-template.md (skill guidance and the nested-workflow guard), install.md, analyze-me-with-gjc.md, bot-integration.md, brand-assets.md, and the README skill table plus its workflow diagram. Lore-id: c7b4f9c8 Constraint: dated records stay untouched -- CHANGELOG entries, docs/prompt-architect-reports/ audits, REBRANDING_PLAN and benchmark fixtures document past behavior and rewriting them would falsify history Confidence: high Scope-risk: narrow Reversibility: easy Directive: verify AC-27 with a pattern that catches bare `team`, not just the command forms -- the narrow grep is what let this through twice Tested: 125 tests green across default-gjc-definitions, the autoresearch suites and signal-teardown; types, check:tools, check-visible-definitions, verify-gjc-skill-docs, verify-gjc-plugins and rebrand-inventory --strict all pass. The exhaustive sweep now returns only an asset filename and the word "gateways"
The terminal critic's OKAY listed one non-blocking polish item: the Windows psmux troubleshooting note carried "team guarantees" twice on the same line and the previous sweep fixed only the first. Shipping a known residual after being told about it is not acceptable, so it goes now. Lore-id: c7c1a2f6 Confidence: high Scope-risk: narrow Reversibility: easy Tested: docs-index regenerated; docs/install.md now has zero team references
…ent probe The mandatory computer-use red-team gate fires on any edit to settings-schema.ts, and this branch edits it. That gate is not ceremony here: settings-schema.ts defines the computer safety envelope itself (computer.enabled, computer.alwaysOn, computer.killSwitchHotkey, computer.auditLog.enabled, computer.screenshotMaxBytes, computer.screenshotGc.*), so an edit there could weaken the kill switch. Proving it did not is a real obligation, and it is satisfiable with real tests rather than asserted away. Running the repo's own seven-case probe surfaced a pre-existing failure: permission-revoked has been failing at every commit, reproduced on a clean worktree at base 4c16e70. The cause is not a safety defect. The COMPUTER_PERMISSION_REQUIRED guidance was reworded to the more specific "Grant Screen & System Audio Recording or Accessibility to the exact GJC launcher..." wording, and the probe still asserted the old sentence verbatim. The archived report at artifacts/vb001-gen5 still shows the old message passing, which dates the drift. The probe now asserts the substance of the contract -- the guidance must name Recording and Accessibility -- instead of one exact sentence, so a future rewording cannot silently break it again while a dropped permission name still fails it. The new suite covers all seven invariants against the real policy and validation layers: enablement/kill-switch refusal, repeat-attempt enforcement, mid-session revocation with no stale permission cache, stale-display refusal and screenshot GC, pre-dispatch coordinate bounds across click/double_click/move/drag-both-ends/scroll including edge and negative values, bounded action sequences, and blast radius via screenshot byte caps plus audit-record presence and absence. It also asserts each computer safety key still exists in the registry with its expected type and default -- the direct falsification test for the edit that triggered this gate. Lore-id: c8a3d7e2 Constraint: test-only; no computer source touched, and no real desktop input, pointer movement, or live screenshot is driven Rejected: report permission-revoked as failed and escalate | the failure is a stale assertion, not a safety defect, and shipping a known-red probe hides real regressions Rejected: mark the case not_applicable | the gate rejects that outright, and correctly Confidence: high Scope-risk: narrow Reversibility: easy Tested: 20 tests green across computer.enforcement and computer.redteam; the seven-case evidence report regenerates with all seven passed; types and biome clean. The pre-existing failure was isolated in a detached worktree at base to prove it predates this branch
Lore-id: c8d4a7f1 Confidence: high Scope-risk: narrow
…the shell Two suites depended on an ambient GJC_SESSION_ID and only passed because I authored them inside a live GJC session, where that variable is set. On a clean runner the mission resolver raised SessionResolutionError, which the python tool's fail-closed catch reported as an error result -- so CI failed seven tests that were green locally. Both files now pin GJC_SESSION_ID in beforeAll and restore the previous value in afterAll, matching the pattern already used by runs, report, capabilities and the runtime suite. Verified by running with the variable explicitly unset, which reproduces the CI failure before the fix and passes after. Lore-id: c9b8d2a7 Constraint: tests must not read ambient session state; pin it or pass it Confidence: high Scope-risk: narrow Reversibility: easy Directive: run `env -u GJC_SESSION_ID bun test ...` when a suite touches workflow state -- a green local run inside a GJC session proves nothing about a clean runner Tested: env -u GJC_SESSION_ID over test/autoresearch/ is 85/85, and 273/273 across the runtime suite, signal-teardown, default-gjc-definitions, cli-command-surface, gjc-skill-state-hooks, both computer suites, the descriptor registry, psmux-detect, workflow-hud-summary and the TUI autocomplete suite
…wnership
The mission `python` tool shipped with no operator doc, no model-facing
prompt file, and a kernel-ownership model that `docs/python-repl.md` still
described as session-keyed only. Three concrete gaps, all closed here.
`src/prompts/tools/python.md` is new, and the tool now imports it with
`{ type: "text" }` instead of carrying an inline description string. Every
other builtin already keeps its model-facing prompt in that directory, and
AGENTS.md forbids building prompts inline -- this tool was the exception.
`docs/tools/python.md` is new and matches the sibling per-tool docs: the
discoverable/defaultInactive registration and why activation must pass the
full merged tool list, the `execute`/`clear` actions with no separate
teardown tool, per-call mission resolution, and the fail-closed contract
that a missing or corrupt mission never degrades to an ad-hoc kernel.
`docs/python-repl.md` gains a Kernel ownership section distinguishing
session-owned kernels (`eval`) from explicitly-owned ones
(`autoresearch:<mission-id>`), and records that signal exit drains both
tool-cleanup registries -- the transition registry is where the SDK
actually puts a tool's `registerSessionCleanup`, and omitting it orphaned
the subprocess on Ctrl-C while graceful dispose looked fine.
`docs/tools/eval.md` gains a pointer so the two Python-running tools are
not confused.
Lore-id: c9c4e1b6
Constraint: documentation must match implemented behavior exactly -- every
claim here was read back out of the shipped code, not the plan
Confidence: high
Scope-risk: narrow
Reversibility: easy
Tested: 209 tests green across the autoresearch suites, the mission
runtime, signal-teardown, default-gjc-definitions, cli-command-surface,
the descriptor registry and both computer suites; check:types and
check:tools both pass, with the remaining biome warnings all in
dev-only files. Verified the description now resolves from the .md and
that the regenerated tool catalog and docs index both carry it
Expose the durable mission lifecycle through the CLI and align research-only guidance with its enforced contract.\n\nLore-id: pr4430-terminal\nConstraint: autoresearch never modifies product code\nConfidence: high\nScope-risk: wide\nReversibility: easy\nTested: coding-agent check:types and focused autoresearch/default-definition/CLI/skill-state/catalog suites
Scope mission kernels and artifacts by GJC session, persist public run/verdict state, and remove stale controller/catalog drift.\n\nLore-id: pr4430-terminal\nConstraint: autoresearch remains research-only\nConfidence: high\nScope-risk: wide\nReversibility: easy\nTested: coding-agent check/build; 279 focused autoresearch, CLI, definitions, controller-docs, pipeline, and computer safety tests
…ug alone
TerminalReviewA66 blocker 1. The live discoverable mission Python tool
derived its kernel owner as `autoresearch:<slug>`, while the public `clear`
verb disposed `autoresearch:<sessionId>:<slug>`. Two consequences, both
user-visible: `gjc autoresearch clear` never reached the running kernel and
left it resident, and two concurrent sessions on the same mission slug
shared one live kernel and therefore one Python namespace.
The mission-bound path had smuggled the session scope into the mission id
as `${sessionId}:${slug}`, so it happened to match `clear` while the
discoverable builtin did not. Both paths now go through one derivation,
`autoresearchKernelOwnerId(sessionId, missionId)`, and the session id is a
first-class field on the mission context rather than string-concatenated
into an id.
`AutoresearchReadReceipt` now carries the resolved `sessionId` so callers
derive owner identity from what was actually read instead of re-resolving
and drifting again.
Lore-id: cb2e8f41
Constraint: exactly one owner-id derivation; an inline template in either
path is what caused this
Rejected: make `clear` use the slug-only owner | would reintroduce
cross-session kernel sharing
Confidence: high
Scope-risk: moderate
Reversibility: easy
Tested: autoresearch suites 89/89 plus the mission runtime suite. Two new
regression tests -- one asserting the live tool and `clear` derive the
same owner, one asserting two sessions on the same slug get different
owners -- proven fail-today by reintroducing the slug-only derivation
(4 fail, then pass once restored)
Installing defaults only ever wrote the CURRENT set, so a definition dropped from the bundle stayed under the agent dir forever. `team` was the first removal, so it was the first to expose the gap: after upgrading, `~/.gjc/agent/skills/team/SKILL.md` was still on disk, filesystem skill discovery still found it, and `/skill:team` still resolved -- pointing at a workflow whose command and runtime no longer exist. Retirement quarantines rather than deletes. The directory moves to `<targetRoot>/retired/<name>.<timestamp>/`, so a user who customized the skill keeps their content and nothing is destroyed to satisfy a rename. The timestamp means repeated retirements never overwrite an earlier quarantine. `--check` reports what would move without touching the filesystem, and `setup defaults` prints where each retired skill went. Also sweeps the three translated READMEs, which the original change missed: each still advertised `team` in its four-skill table, an opt-in `gjc rlm` mode, and `gjc_delegate_team`. Only the English README had been updated. Lore-id: ca1f7b3d Constraint: never delete a user's skill directory to complete a rename -- quarantine and report instead Rejected: delete the retired directory outright | destroys a customized local skill with no recovery Rejected: delete only when content matches the old bundled copy | we no longer ship that content, so there is nothing to compare against Confidence: high Scope-risk: narrow Reversibility: easy Directive: dropping a bundled default now requires adding it to RETIRED_GJC_DEFINITION_NAMES, or it lingers as a discoverable ghost skill Tested: default-gjc-definitions 33/33 including four new cases -- quarantine leaves the four current skills intact, --check stays read-only, absent retirees create no retired/ dir, and a second quarantine does not collide with the first. All four proven fail-today by disabling the retirement call. Verified end to end against a real installed layout: team disappears from skills/, the four current skills install, and the stale content survives under retired/. Types clean and the translated-README acceptance greps return nothing
Lore-id: cb3a9d52 Confidence: high Scope-risk: narrow
TerminalReviewA66 blocker 2. `clear` removed only `mission.json` and left `ledger.jsonl`, `runs.jsonl`, `experiment.json` and `runs/` in place, so a successor mission in the same session inherited the previous mission's events. `extractVerdictFromLedger` would surface a prior `verdict_issued` as if it belonged to the new mission, and run history plus the metric contract leaked the same way. Clear now quarantines the entire working set to `<dir>/retired/<slug>.<timestamp>/` rather than deleting it, so a completed mission stays auditable while the successor starts from genuinely empty state. The `kernel_cleared` row is appended to the OUTGOING ledger before it moves, so the retired copy is a complete record ending with its own clear instead of the clear vanishing from the audit trail. Writing the tests found a real defect in my own earlier retirement work: a timestamp-only directory name collides when two retirements land in the same millisecond, silently overwriting the earlier quarantine. Both this path and the bundled-definition retirement now reserve the directory with an exclusive mkdir and disambiguate with a counter. Lore-id: cb5d1e83 Constraint: never delete mission evidence to satisfy a restart -- quarantine and report where it went Rejected: keep the ledger and filter by mission slug on read | leaves prior verdicts one bug away from resurfacing and grows unbounded Confidence: high Scope-risk: moderate Reversibility: easy Tested: 149 tests green across the mission runtime, the autoresearch suites and default-gjc-definitions, stable over three consecutive runs (the collision defect was intermittent, so repeat runs are the evidence). New cases cover: successor ledger empty after clear; retired ledger ends with its own kernel_cleared; a successor cannot see the prior verdict; clear with no mission is a no-op that creates no retired dir; two clears retire to distinct directories
…blic lifecycle TerminalReviewA66 blocker 3. `createAutoresearchExperimentConfig` already accepted `primaryMetric`, `metricUnit` and `direction`, but `AutoresearchMission` had no metric fields, so nothing the public intake collected could reach it. `autoresearchRunsStore` hardcoded `primaryMetric: "metric"` and inherited lower-is-better, meaning a mission whose research contract is higher-is-better silently optimized backwards and reported the wrong best run. The mission artifact now carries an optional metric contract, both intakes accept it (cold `write` fields, and `autoresearch-metric` / `autoresearch-metric-unit` / `autoresearch-metric-direction` spec declarations mirroring the existing `autoresearch-mode:` style), and the values are threaded into the experiment config. Direction is validated at the write boundary exactly like mode: an unrecognized value is rejected, not coerced, so a typo cannot quietly restore the default. Read normalization preserves the contract so it survives round-trips. The fields stay OPTIONAL: a mission that declares nothing behaves exactly as before. Lore-id: cb7c4a96 Constraint: metric direction is never inferred or coerced -- same boundary discipline as mode Rejected: default to higher-is-better when a unit looks like a rate | that is inference, which is what the mode rule exists to forbid Confidence: high Scope-risk: moderate Reversibility: easy Tested: 154 tests green across the mission runtime, autoresearch suites and default-gjc-definitions; types and check:tools clean. New cases cover a declared contract reaching the experiment config, the untouched default path, boundary rejection of an invalid direction on both intakes, and spec parsing. Proven fail-today by restoring the hardcoded metric name
…lation TerminalReviewA66 blocker 4. The SKILL.md and AGENTS.md both state that autoresearch produces findings and a verdict and never product code, but nothing enforced it: `isPlanningSkill` deliberately excluded autoresearch, so a mission could freely edit the user's working branch. A test even asserted that contradiction as intended behavior. A blanket block is not the answer -- `bash` is a gated tool, so it would break the `autoresearch.sh` harness that experiments legitimately need. Branch isolation is the authorization instead: on an `autoresearch/*` branch every edit is contained and revertible through the existing keep/discard machinery, so mutation is allowed; off that branch the mission would be editing the user's working branch and is blocked with an actionable message. Terminal mission phases release normally. The branch is read live from the worktree via the existing `getCurrentAutoresearchBranch`, not from recorded mission intent, because a user can switch branches mid-mission and recorded intent would authorize mutation that is no longer isolated. Lore-id: cb9f2d74 Constraint: enforcement must not break the harness -- experiments are the workflow's actual work, so the exemption is branch-scoped rather than tool-scoped Rejected: block outright in every phase | breaks autoresearch.sh and the data lane, making the workflow non-functional Rejected: authorize from a recorded mission branch marker | records intent, not the current worktree, so switching branches silently defeats it Confidence: medium Scope-risk: moderate Reversibility: easy Directive: this is a security-relevant guard -- keep the branch check live; do not cache it into mission state Tested: 215 tests green across the mutation guard, mission runtime, the autoresearch suites, default-gjc-definitions and skill-active-state; types, check:tools, check-visible-definitions, verify-gjc-skill-docs, verify-gjc-plugins, ci-gjc-state-gates and rebrand-inventory --strict all pass. Replaced the test that asserted the old exclusion with three real cases (blocked off-branch, allowed on an autoresearch branch, released at terminal phases); proven fail-today by re-excluding autoresearch Not-tested: a live end-to-end harness run on a real autoresearch branch
Regenerated output differs from the committed copy by one newline in the task-tool agents block. Generated files are generator-owned, so the regenerated text wins. Lore-id: cba1e7f0 Confidence: high Scope-risk: narrow
… git The blocker-4 unit coverage stubs `getCurrentAutoresearchBranch`, so it proves the guard's branching logic but never that the real git read wires up. Since the authorization is deliberately read live from the worktree rather than from recorded mission intent, a stubbed test cannot catch a regression in the read itself -- which is the whole mechanism. This suite drives the full path with nothing mocked: real `git init`, a real isolation branch created through the production `ensureAutoresearchBranch`, real `git checkout`, and the real guard decision. It covers blocked on the user's branch, allowed once isolated, re-blocked the moment the worktree leaves the branch mid-mission, a lookalike `autoresearch-scratch` branch that must NOT authorize, and terminal-phase release. Writing it surfaced a real property of the production helper worth pinning: mission state files are untracked, and an untracked-dirty worktree makes `ensureAutoresearchBranch` take its degraded path and stay on the current branch. The fixture therefore gitignores `.gjc/` exactly as a real GJC repo does; without that the isolation branch is never created and the authorization silently never applies. Lore-id: cbc3f8e1 Constraint: no stubbing in this suite -- the point is the real git read Confidence: high Scope-risk: narrow Reversibility: easy Tested: 4/4 in the new suite; 157 tests green across the autoresearch suites, the mutation guard and the mission runtime, stable over two runs. Proven bidirectionally fail-today: re-excluding autoresearch from the guard fails 3 (enforcement gone), and removing the branch-isolation exemption fails 2 (legitimate experiment work wrongly blocked)
…rface The extragoal and dogfood template tests still pinned the retired team skill, failing CI shard 1 after the fourth canonical slot moved to autoresearch. Lore-id: 3f8a2b1e Confidence: high Scope-risk: narrow Reversibility: trivial Tested: both template tests pass against the bundled skills directory
95a40f6 to
1b183cc
Compare
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
gajae.pr-review-verdict.v1 merge-blocked sha256:f18a34de4cfb4745ff6baa2e877673f98f0eda6494dbcfa0e63f94b53601ac99 reviewer:human reviewer-id:Yeachan-Heo evidence:Exact head 95a40f6 is rebased on origin/dev 91a3511. Retired bundled Team definitions now fail defaults checks; exact-head CI and independent approval are pending. Owner-authorized DRAFT HOLD.