Features v0.2.2 - #1
Merged
Merged
Conversation
read_file / write_file / edit_file Read, create/overwrite, and exact-string edit (with replace_all, unique-match enforcement, literal $) list_dir / grep Directory listing + bounded regex content search (skips node_modules/.git/binaries) bash Shell in the desk's cwd — opt-in, off by default write_memory Appends durable notes to memory.md via new HiveManager.appendMemory() — the exact gap behind "memory not working"
Host side of the native web_search tool: a Brave Search API impl (src/main/webSearch.ts) injected as the searchWeb toolkit dep, gated by a new webSearchEnabled config flag with the key stored under a reserved, redacted 'web-search' credentials id. Adds a Settings 'Web search' section (toggle + key input) and walks the web_search tool to the web-portal glyph. Governance (control gate + breaker) applies unchanged via executeToolFor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…picker Settings now has a 'Native shell (bash)' on/off toggle (mirrors the web-search toggle), so the executor's 'enable in Settings -> native bash' message is finally true. The model picker now distinguishes provider-native capability tags (tooltip 'hosted by <provider>'s API') from harness-provided chips shown for native (non-Claude) models: '+web search' (lit when webSearchEnabled + a Brave key), '+files . grep' (always on), '+shell' (lit when nativeBashEnabled). The picker reads config on mount (same pattern as credential presence). Resolves the 'web search struck-through but I enabled it' confusion. Host-only; no package changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reamble Native desks' bash tool ran cmd.exe on Windows, so ls/head/pipes failed. resolveBashEnv() now detects a real shell (Git Bash, deliberately NOT System32 WSL bash which would remap the cwd) and feeds it as the bashShell dep. A one-line environment briefing (platform + shell + command style) is threaded host->worker via env and appended to the native preamble, so each desk uses the right commands first try. Falls back to PowerShell/cmd with a matching note. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ection The terminal used the WebGL renderer only because VT323 (incomplete glyphs, no real bold) sheared under the DOM renderer — but WebGL's selection-during-scroll is unreliable. Bundle Departure Mono, a COMPLETE retro pixel monospace (box-drawing 128/128, block elements 32/32) with bold mapped to the regular face, and drop the WebGL addon. The DOM renderer now keeps the grid aligned AND gives reliable select / drag-through-scroll / copy. Font is local (CSP 'self'). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part 1 — native trace is now a narrative. Instead of read_file in {"path":"CLAUDE.md"}, each tool call now reads as a collapsible card:
Human summary + status icon + duration: ✓ Read CLAUDE.md · 3ms, ✗ Ran find . -type f | head -60 failed, ⟳ Ran npm test running… — driven by the new toolSummary.ts (covers every toolkit tool + mcp__* + unknown fallback, unit-tested).
Raw args/output collapsed by default, expand on click — and failures auto-expand so the error is always visible (with an effect handling the live pending→failed edge).
Turn dividers — a subtle "turn N" separator between turns (foldEvents.ts emits a turn-divider entry), so the flat transcript reads as grouped turns.
Prose stands out now that tool mechanics are collapsed by contrast.
The structured tab reuses the same summarizer, so both views speak the same language.
Virtualization, stick-to-bottom, and the measured-height infra all still work (cards re-measure on expand like the thinking rows already did).
Root cause of the blank Michael terminal: with a DeepSeek fleet default, god is routed to the native runtime (no Claude PTY), but the god lifecycle was Claude-PTY-only. Now: (1) a native god is booted via cth.nativeSend (not submitToPty/writePty, which has no PTY to write to) with a native-flavored orientation; (2) the orchestrator ROLE is injected into the native god's system prompt host-side (workerEnv is now per-agent; NATIVE_GOD_PROMPT for the isGod desk; the worker prepends it). Messaging already routes to the 'god' worker via native:send. The assistant (Dwight) is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…fault The main spawn router resolves a desk's model as (agent.model ?? config.defaultModel ?? role), so a model-less god routes to the native runtime when the fleet default is native (DeepSeek) — and runs fine. But the renderer's runtime-kind check (isNativeRuntimeDesk) used ONLY agent.model, so a model-less god classified as Claude → rendered the blank Claude PTY view (no 'native ·' trace) and the composer routed messages to the dead writePty path. Mirror the router's fallback: resolve agent.model || fleetDefaultModel (now in the store, set at config load) in both AgentDetailPanel and MessageQueueComposer. A native god now shows its transcript and accepts messages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A native god (non-anthropic fleet default) has no real PTY, but its agent record carried ptyId: GOD_PTY — so the live-PTY reconcile dropped it on every reload, and the auto-respawn then failed because the native worker still exists in MAIN ('native worker exists'), so god was never re-added and vanished. Now: the god record carries NO ptyId when native (reconcile keeps it; runtime kind is derived from the model), and a respawn that reports 'worker exists' RESTORES the record (refreshing model) + marks ready without re-kicking-off, instead of failing. This also refreshes a stale empty-model record that previously rendered the blank Claude-PTY view.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…authoring); per-task branches; scale safety Corrects the worktree model so the integrator/reviewer aren't isolated (fixing the phyllis `addWorktree failed` error) and a worker's tasks don't pile on one branch. Isolation (Part 1): only code AUTHORS (worker/planner/qc) get a per-desk worktree. The integrator (merges into the trunk host-side) and reviewer (read-only) run in the base tree on the trunk. New roleAuthorsCode predicate; pty:spawn gates isolation on it; roster reports a branch only for authors. Integrator = gate+merge, not author (Part 3): new roleCanWriteFiles (authors only) gates write_file/edit_file (incl. SDDP specs/); roleCanEditCode still gates bash (the integrator keeps bash for its test gate + hive_integrate). Split applied in the native deny-list, agentTools resolveWrite (canWriteFiles dep, falls back to canEditCode), and the Claude PreToolUse hook. So an integrator can bash + merge but cannot author the trunk. Per-task branches (Part 2): a card's `branch` is the deterministic agentTaskBranch (`agent/<id>-<cardId>`, hyphen-joined to avoid a D/F conflict with the desk's home branch), stamped on add + reassign. Worker prompts (native + Claude, std + SDDP) tell desks to author each card on its own branch off the trunk; the integrator merges that branch. Replaces the per-desk branchFor stamping (dep removed). Recovery (Part 4): resetBaseToTrunk now stashes a dirty base tree before checking out the trunk (single recovery action); the migrate-to-worktree path/IPC/button is removed (a freed branch re-isolates on next spawn). Settings → Worktrees not-isolated row → Reset. Scale (Part 5): merges into a repo are serialized (per-repo merge lock) so multiple integrators can't race the index; a clean merge deletes the merged branch (deleteMergedBranch, best-effort). A feature's planner/QC kickoff routes to a SINGLE holder (multiple would clobber the shared base specs/<feature>/). Docs (Part 6): orchestration-model.md updated. SDDP specs/ sharing is already handled by the existing redirect, so planner/qc isolation stays safe. (won-agent-core source — roleAuthorsCode/roleCanWriteFiles/agentTaskBranch + hiveTools/ agentTools + tests — is its own repo; commit it there. App consumes it live via the alias.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Give native (DeepSeek) desks the sddp27 sub-agents for real: a spawn_subagent tool forks an ephemeral one-shot worker with the named specialist's prompt, runs it caller-scoped, and returns its final text — the native equivalent of Claude's Task tool. Feature epic cards carry a milestone checklist, each step hard-gated on its real specs/<feature>/ artifact. Host side (main repo): - subAgentRunner.ts: runOneShotSubAgent — reuses NativeAgentWorker over the electron transport; captures final text on terminal stop, kills the idle process; nesting guard + concurrency cap + wall-clock timeout. Plus the pure resolveSubAgentModel helper. - index.ts: agentToolDeps.spawnSubAgent (child inherits caller provider+model, runs under executeNativeToolFor(callerId) so specs/ redirect + worktree resolve to the caller; deny-list incl. nesting/integrate/board-mutation); extracted executeNativeToolFor; featureArtifactExists + deskHoldsFeature deps; agentModels map so a sub-agent inherits the caller's exact model; child token cost rolls up to the caller via the telemetry collector. - agentWorker.ts: consume NATIVE_AGENT_SUBAGENT_PROMPT + optional per-spawn caps env. - toolGating.ts: deny spawn_subagent unless the desk holds an orchestrating role. - config.ts: sddpSubAgentModel (same-provider override, else caller model). - TasksKanban.tsx: read-only milestone-pill row on the feature banner. Tests: toolGating spawn_subagent advertising; subAgentRunner over a fake transport (capture/kill/nesting/timeout/token-rollup/model-resolution). Full suite 442 green; typecheck + lint clean. NOTE: the bulk of the logic (subAgents registry, FeatureMilestone/advanceMilestone gate, spawn_subagent tool + specs feature-scope gate) lives in packages/won-agent-core, which is gitignored here and has its own (currently uncommitted) repo. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Complete the SDDP feature pipeline on top of the native sub-agent runtime:
- index.ts: parseFeatureTasks dep — parse a feature's specs/<feature>/tasks.md
`- [ ] T###` lines (stripping [P]/[US#]/{FR-###}/[after:…] to the description)
so hive_import_tasks can turn the plan into implement cards; updated the SDDP god
prompt to call hive_import_tasks directly.
- docs/orchestration-model.md: document the Authors vs Coordinators role classes,
the native sub-agent runtime (spawn_subagent), the milestone pipeline + optional
steps, and hive_import_tasks; refreshed the implementation map + deferred list.
Package side (won-agent-core, gitignored here): FeatureMilestone.optional + the full
SDDP_MILESTONE_TEMPLATE (spec→clarify→research→data-model→contracts→adrs→plan→
checklist→tasks→implement→qc); the advanceMilestone gate skips the artifact check for
optional steps + activates the next-in-order milestone; hive_import_tasks tool +
parseFeatureTasks dep; Authors/Coordinators doc comment.
Full suite 446 green; typecheck + lint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…host side)
P1–P3 built + tested the sub-agent runtime + milestone pipeline, but NOTHING drove
it: no role prompt mentioned spawn_subagent, no card ever got milestones, no prompt
mentioned advanceMilestone — so SDDP mode ran the old melded-prompt behavior and the
machinery sat inert. P4 wires the orchestration layer:
- sddpPrompts.ts (new, extracted from index.ts so it's unit-testable): the SDDP role
prompts now INSTRUCT the planner/qc/worker to spawn_subagent their specialists per
phase (database-administrator/api-designer/adr-author/wbs-generator/spec-validator
for the planner; qc-auditor/story-verifier for qc; developer for the worker) and to
advanceMilestone after each artifact; the god creates the feature EPIC card
(hive_add_task {epic:true}) and runs hive_import_tasks. A shared SDDP_SUBAGENTS note
is added only for spawn-capable roles (roleCanSpawnSubagents).
- index.ts: imports the prompts (removed the inline consts).
- hive.ts: Claude god parity — sddpRoleLine now creates the epic card + hive_import_tasks.
- sddpPrompts.test.ts (new): regression guard — asserts the prompts keep naming
spawn_subagent + the specialists + advanceMilestone + epic, so this can't rot back inert.
- docs/orchestration-model.md: the runtime/milestone sections now describe how it's
DRIVEN + an end-to-end feature walkthrough; map + deferred refreshed.
Package side (won-agent-core, gitignored here): hive_add_task {epic} seeds
defaultMilestones(); the implement-only doing-gate exempts the epic card; tool
descriptions name the spawn->advance flow.
Full suite 453 green; typecheck + lint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… side) The milestone checklist becomes the program; a host engine is the interpreter, so the SDDP workflow is deterministic host code rather than "trust a desk to follow a prompt." P1 ships the engine + the mechanical host-step chain. - sddpPipeline.ts (new): SddpPipeline.advanceFeature — find the feature epic, validate it has a usable native owner desk (creds+model; escalate loudly if not), and for the active HOST milestone (research/data-model/contracts/adrs/checklist/tasks) run its specialist AS the owner, gate on the real artifact, advance (which re-triggers the engine to chain); skip an optional step that produces nothing; escalate once when a required artifact never lands. Debounced schedule + per-step in-flight lock. - hive.ts: setPipelineTrigger (fired per feature from notifyTaskTransitions, fire-and- forget) + advanceMilestone(epicId,key) (reuses the shared advanceMilestones helper, writes via writeTasks which re-triggers the engine). - index.ts: construct SddpPipeline (owner-usable = spawned non-Claude desk w/ model; reuses agentToolDeps.spawnSubAgent/featureArtifactExists) + wire the trigger. Package side (won-agent-core, gitignored): advanceMilestones + milestoneDriver + HOST_DRIVEN_MILESTONES in types.ts; hive_update_task's advance gate refactored onto the shared advanceMilestones (no behavior change). Tests: sddpPipeline (run/gate/advance/chain, optional-skip, desk-skip, owner-escalate, sddp-off, in-flight lock) + the advanceMilestones/milestoneDriver helpers. Full suite 463 green; typecheck + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The engine now drives the whole Plan→Tasks chain, authoring artifacts into sddp27's real structure instead of "from memory." - sddpPipeline.ts: buildStepInput prepends the step's TEMPLATE (when one exists) as "author into THIS structure", and adds the `plan` step (read spec + the design artifacts → assemble plan.md). Templates are supplied as data, not baked into prompts. - docs/orchestration-model.md: documents the host-driven engine (program=milestone checklist, interpreter=sddpPipeline; owner-validate → run host step → gate → advance → chain; templates-as-data; escalation) + plan now host-driven. Package side (won-agent-core, gitignored): new sddpTemplates.ts (condensed plan + tasks templates + templateForStep); plan-author sub-agent (assembles plan.md into the template, every requirement in the Coverage Map); SDDP_MILESTONE_TEMPLATE plan → subAgent plan-author + `plan` added to HOST_DRIVEN_MILESTONES (so the chain no longer stalls at plan). Tests: plan-author registry + gateArtifact, templateForStep(plan/tasks) structure + null for single-artifact steps, milestoneDriver(plan)=host, the engine runs plan-author for the plan step (template rides in the input) + advances on plan.md. Full suite 467 green; typecheck + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…topilot) + Human-Gates Registry The engine now drives Specify→Tasks end-to-end, and the Clarify human gate is flippable. - sddpPipeline.ts: `spec` runs as a host step (spec-author drafts spec.md from the epic's description = the feature request, into the spec template). New runHumanGated branch for Clarify: autopilot ON → spec-author resolves the [NEEDS CLARIFICATION] markers with documented defaults + advance; autopilot OFF (default) → run requirements-scanner once, askHuman (needs_human), and PAUSE until the operator answers + advances. New deps autopilot()/askHuman; buildStepInput gains the spec + clarify cases + a ctx.request param. - config.ts: sddpAutopilot (OFF by default) — the master flag for the Human-Gates Registry. - index.ts: wire autopilot()=readConfig().sddpAutopilot + askHuman → hive.send(needs_human). - sddpPrompts.ts (native god): epic carries the feature request in `description`; on a Clarify needs_human message, answer + advance the clarify milestone. - docs/orchestration-model.md: the Human-Gates Registry (every human-in-the-loop point + its autopilot path; engine owns #2 Clarify today, #3-5 attach to the same flag in P4). Package side (won-agent-core, gitignored): spec template in sddpTemplates; spec-author sub-agent; SDDP_MILESTONE_TEMPLATE spec→spec-author + `spec` host-driven; FeatureMilestone .humanGated + clarify humanGated:true. Tests: spec-author registry + spec template; milestoneDriver(spec)=host + clarify humanGated; engine spec-step (request+template in input → advance), Clarify autopilot-OFF (ask once, no advance, dedup) + autopilot-ON (spec-author resolves → advance, no ask). Full suite 471 green; typecheck + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t side) Completes the engine's reach honestly. Implement/QC/Integrate are distributed, branch- based work (workers on per-card branches → the notifier-pinged qc desk writes .qc-passed → the integrator merges) — not "one sub-agent → one artifact", and a host-driven qc-auditor would deadlock (the .qc-passed gate blocks integrate, but QC needs the merged code). So the engine FEEDS + TRACKS the tail instead of running it: - sddpPipeline.ts: trackDistributed — at `implement` it SEEDS the implement cards from tasks.md once (deterministic; new seedImplementCards dep) so the work exists, then advances the implement/qc pills when the real markers (.completed/.qc-passed) land. No sub-agent; the distributed flow does the work. Escalates if tasks.md has no tasks to import. - index.ts: seedImplementCards — parseFeatureTasks → one feature-tagged todo card per task (unassigned; the god routes them) → writeTasks. - sddpPrompts.ts + hive.ts: the god no longer runs hive_import_tasks (the engine seeds); it ASSIGNS the seeded cards to workers. QC + integrate proceed via the existing flow. - docs: the engine authors spec→tasks + seeds + tracks; the distributed flow does implement/QC/integrate; registry #3 (spec/plan review) is not an engine gate, #4/#5 are the per-card board flow. Deferred (documented): true host-driven QC (needs a QC-integration worktree to dodge the merge-order deadlock); auto-assign of seeded cards; a poll for crisper pill tracking. Tests: implement seeds when no cards (no advance) / escalates on 0 tasks; implement+qc advance on .completed/.qc-passed; qc waits while the marker is absent. Full suite 476 green; typecheck + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…use/stop (host side) Operator control + observability on the engine, plus it now assigns work (not just seeds). Per-step control (rides the milestone object + the board pills): - sddpPipeline reads the active step's control first: paused = hold (in-flight finishes); stopped = ABORT the in-flight sub-agent (AbortSignal kills the ephemeral worker) + hold; manual = engine skips the run, advances when the artifact appears (a desk/human does it); auto = drive as designed. Plus a live per-feature status (running/waiting/paused/stopped/ manual/blocked) the engine maintains + exposes via pipeline:status. - subAgentRunner: runOneShotSubAgent accepts an AbortSignal (kill + resolve aborted); spawnSubAgentFor extracted in index.ts so the tool (no signal) + the engine (with signal) share it. Driven + assigned: - the engine auto-assigns the epic to a usable planner desk (resolveOwner -> findDeskForRole + assignCard) and round-robin-assigns the seeded implement cards to worker desks (seedImplementCards, per-card branch stamping). usableDesksForRole/desksForRole helpers. UI: the milestone pills become interactive — pause/stop/switch(manual)/resume buttons that set milestone.control + persist via hive:writeTasks, plus a live status glyph from pipeline:status. parseTasks parses control; the board polls pipeline:status alongside tasks. Package side (won-agent-core, gitignored): FeatureMilestone.control field. Tests: subAgentRunner abort (in-flight kill + pre-aborted no-fork); sddpPipeline control (paused waits, manual skips+advances-on-artifact, stopped aborts in-flight) + auto-assign (epic->planner, escalate when none). Full suite 483 green; typecheck + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ree) An isolated author/qc desk runs cargo (etc.) inside its git worktree, so `target/` churned under harnessHome (S:\munderdiff\worktrees\<id>) — heavy, executable, and real-time-scanned by antivirus, which LOCKED the files and failed the build: could not execute … build-script-build … Access is denied. (os error 5) cargo clean … being used by another process. (os error 32) (This was the source of the earlier "rust build in S:\munderdiff" AV ping — the QC desk.) Fix: inject a build-tool env (today CARGO_TARGET_DIR) pointing at ONE cache root — config.buildCacheDir, else <harnessHome>/build-cache — subdivided per working tree (buildCacheKey = <basename>-<hash(normalizedCwd)>, one builder per dir so cargo's per-target lock never contends; the hash discriminates same-named repos). Net: the operator excludes that single folder from antivirus; nothing builds into a worktree or the repo (non-isolated base-tree desks get a repo-keyed dir too). - paths.ts: buildCacheKey(cwd) (pure, normalized-path keyed). - index.ts: buildCacheRoot()/buildCacheEnvFor(cwd) (mkdir + CARGO_TARGET_DIR); wired as the agentToolDeps.bashEnv (native bash runs in MAIN) AND merged into opts.env at pty:spawn after the worktree override (Claude author + base-tree desks). - config.ts (+ preload/renderer mirrors): buildCacheDir?: string. - docs/orchestration-model.md: build-output-redirect bullet. Package side (won-agent-core, gitignored): AgentToolDeps.bashEnv?(id) + runBash(command, cwd, shell, env?) merging env over process.env (absent ⇒ inherit). Tests: buildCacheKey (deterministic, collision-safe, win32 case-fold) + bash env merge/inherit. Full suite 489 green; typecheck + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rent folder) Generalizes the hardcoded CARGO_TARGET_DIR redirect (9c14fc6) into a small token-templated env table the user edits in Settings: define ONE parent folder and the per-tool/per-worktree structure is filled in automatically. - src/shared/buildEnv.ts (NEW, renderer-safe): BuildEnvEntry, DEFAULT_BUILD_ENV (CARGO_TARGET_DIR=${buildRoot}/cargo/${worktreeKey}), BUILD_ENV_TOKENS, and expandTokens() (pure ${token} replace — known tokens substituted, unknown left intact). - src/main/buildEnv.ts (NEW): resolveBuildEnv(entries, root, vars) -> { env, dirs } — expands per desk, normalizes absolute values to platform separators, returns under-root dirs to mkdir; skips blank-name rows and ${buildRoot} rows when there's no root. - index.ts: deskBuildEnvFor(cwd, agentId) replaces buildCacheEnvFor — reads config.buildEnv ?? DEFAULT_BUILD_ENV, expands against {buildRoot, worktreeKey, cwd, agentId, harnessHome}, mkdirs, injected at BOTH seams (native bashEnv + Claude opts.env), now passing the desk id. - config.ts (+ preload + renderer store mirrors): buildEnv?: BuildEnvEntry[]. - SettingsModal: a Build-cache folder picker (the one parent folder) + a full Build-env TABLE editor (add/edit/remove rows, token hint, per-row live preview via expandTokens; commit on blur, add/remove/reset immediate). - docs/orchestration-model.md: rewrote the build-output bullet for the generic mechanism + tokens. Tokens are host-side only; the package bashEnv already takes a Record (no package change). Default cargo path moves from <root>/<key> to <root>/cargo/<key> (one-time recompile). Tests: shared expandTokens (substitution/unknown/repeat/missing) + DEFAULT shape; main resolveBuildEnv (default-under-root, external-not-created, blank-name skip, null-root behavior, multi-tool). Full suite 500 green; typecheck + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The token-templated buildEnv table (45cc0a6) is already general (any env var, not just build dirs). This adds an optional PER-PROJECT-REPO override layer: for a desk whose project repo matches, the per-repo table merges ON TOP of the global table (same name wins). The output folder (buildCacheDir) stays global — one AV exclusion; ${worktreeKey} already separates output per repo. - shared/buildEnv.ts: mergeBuildEnv(base, override) — override wins by name, new names appended, base order preserved; empty override ⇒ base unchanged. (Pure; used by the resolver + the renderer preview.) - main/buildEnv.ts: perRepoEntriesFor(map, repo) — the override entries for a desk's repo, matched case/separator-insensitively via normalizeRepoPath (a small scan). - index.ts: deskBuildEnvFor now layers mergeBuildEnv(base, perRepoEntriesFor(cfg.buildEnvByRepo, repoForId(agentId))) before resolveBuildEnv. No seam changes. - config.ts (+ preload + renderer store): buildEnvByRepo?: Record<string, BuildEnvEntry[]>. - SettingsModal: extracted the row editor into a reusable BuildEnvRows component; the global table uses it, plus a new Per-repo env section (repo <select> + the same editor bound to buildEnvByRepo[repo]; "•" marks repos with overrides; empty arrays pruned on persist). - docs/orchestration-model.md: noted the table is general + the per-repo override layer. Host-side only (the package bashEnv already takes a Record — no won-agent-core change). Tests: mergeBuildEnv (replace/append/mix/blank/empty) + perRepoEntriesFor (normalized match across sep/case/trailing-slash, miss/undefined, override-wins-when-merged). Full suite 509 green; typecheck + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…I polish
The env table is general (any var), so rename it to reflect that, add a third override layer,
and make build-dir vs plain-value obvious in the editor.
Rename (buildEnv* → deskEnv*): src/shared/buildEnv.ts → deskEnv.ts, src/main/buildEnv.ts →
deskEnv.ts (+ the two test files). BuildEnvEntry→DeskEnvEntry, BuildEnvVars→DeskEnvVars,
DEFAULT_BUILD_ENV→DEFAULT_DESK_ENV, BUILD_ENV_TOKENS→DESK_ENV_TOKENS, mergeBuildEnv→mergeDeskEnv,
resolveBuildEnv→resolveDeskEnv, perRepoEntriesFor→perRepoDeskEnv, deskBuildEnvFor→deskEnvFor.
Config fields buildEnv→deskEnv, buildEnvByRepo→deskEnvByRepo. KEPT buildCacheDir, buildCacheKey,
and the ${buildRoot}/${worktreeKey} tokens (build-specific). readConfig migrates legacy
buildEnv/buildEnvByRepo so existing configs carry over.
Per-agent layer: config deskEnvByAgent (keyed by agent id). deskEnvFor now layers
mergeDeskEnv(mergeDeskEnv(base, repoOverride), agentOverride) — precedence agent > repo > global.
perAgentDeskEnv lookup added.
${env:VAR} token: expandTokens(template, vars, envLookup?) resolves ${env:NAME} via the host's
process.env (renderer omits the lookup → literal in preview), so PATH=/extra:${env:PATH} appends
safely instead of clobbering. resolveDeskEnv now normalizes + auto-creates ONLY values under the
build root; every other value (PATH lists, flags, external paths) is set RAW (no separator mangling).
UI (SettingsModal): BuildEnvRows → DeskEnvRows with token-insert chips (insert into the focused
row) + a "· created" marker when a value resolves under the build cache; a Per-agent env section
(desk <select> from the live fleet) mirroring Per-repo; "Build env" label → "Desk env".
Deferred (surfaced): secret-backed values (these are plaintext in config.json — not for secrets)
and proxy/CA runtime env (would need a worker-process/PTY seam reaching the model-API call).
Host-side only (no won-agent-core change; bashEnv already takes a Record).
Tests: ${env:NAME} expansion, 3-layer merge precedence, plain-value + ${env:PATH}-raw resolve,
perAgentDeskEnv. Full suite 514 green; typecheck + lint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the env model with two siblings of the desk-env table, plus a shared ${secret:} token.
Secrets vault (encrypted at rest):
- src/main/secrets.ts (NEW): safeStorage codec (enc:/raw: prefix, plaintext fallback) +
set/get/clear/secretNames over config.secrets. Decrypted only in main; values never cross IPC.
- credentials.ts: SafeConfig += secretNames (Omit secrets); redactConfig strips secrets → names only.
- index.ts: secrets:list/set/clear IPC (mirror credentials:*). preload window.cth.secrets.*.
- Referenced anywhere via ${secret:NAME}.
${secret:}/${env:} token:
- shared/deskEnv.ts: expandTokens(template, vars, lookup?) where lookup(prefix,name) resolves
prefixed tokens (env:/secret:); unknown/no-lookup → left literal (renderer preview never sees values).
- main/deskEnv.ts: resolveDeskEnv forwards the lookup. index.ts wires tokenLookup = env→process.env,
secret→getSecretValue.
Runtime env (proxy / custom CA for the agent's OWN calls):
- config.runtimeEnv (global). index.ts runtimeEnvResolved() injected into the native worker process,
the Claude PTY, AND bash (so the model call, Claude CLI, and git/curl all honor it).
- Native fetch is undici (ignores HTTPS_PROXY): agentWorker.ts routes selectAdapter's fetch through an
undici ProxyAgent when a proxy is set; NODE_EXTRA_CA_CERTS works via env (Node TLS). +undici dep.
(NO_PROXY not honored for that one native call.)
UI (SettingsModal): three labeled cards — Secrets (masked add/clear, names-only), Desk env (existing
global/repo/agent), Runtime env (DeskEnvRows with env:/secret: chips, no "created" marker). DeskEnvRows
gains a ${secret:} chip + an "unknown secret" warning when a referenced name isn't in the vault.
config.ts: secrets + runtimeEnv fields. Docs updated (three cards, ${secret:}, proxy/CA caveats).
Tests: expandTokens prefix lookup (env+secret), resolveDeskEnv secret resolve + PATH-raw, redactConfig
strips the vault. Full suite 515 green; typecheck + lint clean. (safeStorage round-trip + worker
ProxyAgent are runtime — verified in-app.)
Host-side only (no won-agent-core change). Deferred: migrate provider keys to safeStorage.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gate) Adds the missing 7th lifecycle phase (sddp27), host-driven: the engine runs the analysis and enforces the gate, rather than trusting a desk to. - New `analyze` milestone between Tasks and Implement (optional; gateArtifact analysis-report.md). - sddpPipeline.runAnalyze (special path like Clarify): (1) MECHANICAL requirement→task coverage in code (analyzeCoverage, pure, won-agent-core) — an uncovered requirement is CRITICAL; (2) runs the registered-but-previously-unused validators policy-auditor (vs project-instructions.md) + spec-validator; (3) writes analysis-report.md with `critical: <n>` frontmatter; (4) GATE: critical>0 holds + escalates (delete the report or set Analyze manual to re-run); autopilot advances with a note; clean advances. A pre-existing report gates without re-spawning the validators. - New engine deps (index.ts): analyzeFeature (coverage), featureArtifactText, writeFeatureArtifact (all path-safe to specs/<feature>/ via featureFilePath). Package side (won-agent-core, gitignored): analyze.ts (analyzeCoverage) + export; analysis-report template (sddpTemplates); the `analyze` milestone (types). Tests: analyzeCoverage (4) + runAnalyze gate (5: clean-advances, uncovered-holds, policy-FAIL-holds, autopilot-advances, pre-existing-gates); existing milestone-order tests updated (tasks→analyze). Full suite 524 green; typecheck + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enforces the rule already in AGENTS.md ("if checklists/ exists, all items complete before Implement").
- sddpPipeline.checklistGate (run from trackDistributed at implement entry, now async): mechanical
scan of specs/<feature>/checklists/*.md completion (checklistStatus dep, `- [ ]` vs `- [X]`); if
incomplete, ONE auto-resolve pass via the test-evaluator sub-agent (deduped per feature), re-scan;
still incomplete → block + escalate (the operator resolves the items, a re-trigger passes) unless
autopilot proceeds. No checklists / already complete ⇒ proceed (seed implement cards as before).
- index.ts: checklistStatus dep (counts unchecked/checked across the checklist dir, path-safe).
Tests: no-checklist N/A seeds (no test-evaluator); incomplete holds + escalates (one auto-resolve);
test-evaluator-completes proceeds; autopilot proceeds; already-complete proceeds without test-evaluator.
Full suite 529 green; typecheck + lint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… QC iterations)
Mirrors sddp27's .github/sddp-config.md as a config object + a compact Settings editor. Consumed by
the host QC + bug-loop phases (P4/P5).
- config.ts: sddpConfig?: { qcStrictness: minimal|standard|strict; coverageTarget; maxChecklist;
maxQcIterations } (+ preload mirror).
- SettingsModal: under the SDDP toggle (shown when SDDP is on), a row of knobs — QC strictness select +
coverage % + max checklists + max QC iterations — persisted as the whole sddpConfig object.
Defaults are built-in (standard strictness, etc.); the values wire into runQc (strictness→required
categories, coverage) and the bug loop (max iterations) in the next phases. Suite 529 green; typecheck +
lint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…deadlock) The engine now RUNS QC instead of only tracking it: it builds a throwaway worktree, merges the feature's implement branches into it (off trunk), runs qc-auditor + story-verifier IN that merged tree, and writes .qc-passed itself on PASS — so the suite tests the INTEGRATED code without touching trunk, resolving the .qc-passed↔integrate deadlock. - git.ts prepareQcTree(repo, wtPath, base, branches): detached worktree at trunk + `git merge` each branch (conflicts are aborted + reported, the rest still merge). removeWorktree tears it down. - sub-agent cwd override: spawnSubAgentFor + executeNativeToolFor take an optional cwdOverride → the child's fs/bash resolve in the QC tree (+ it's a read root); specs/ still anchors to the base repo so qc-report.md lands in the shared specs/<feature>/. No change to non-SDDP runs. - sddpPipeline.runQc (special-cased like Analyze, only when the QC-tree deps are wired — else falls back to the feed-track qc-desk flow): prepare tree → conflicts escalate; else run qc-auditor (build/ lint/tests per sddpConfig.qcStrictness) + story-verifier in the tree → both VERDICT:PASS ⇒ write .qc-passed + advance (integrate unblocks); FAIL ⇒ hold + escalate (+ seedBugCards hook for P5). Idempotent (existing .qc-passed advances); tree always torn down. - index.ts deps: prepareQcTree (branches from the feature's implement cards, base=repoTrunk, tree under <harnessHome>/qc-worktrees), removeQcTree, spawnSubAgentInTree, sddpPolicy. qcTreeRepo maps tree→repo for teardown. Tests: runQc PASS (writes marker, advances, both sub-agents, tree removed), FAIL (holds + escalates + bug-cards hook + tree removed), merge conflict (escalates, no sub-agents), idempotent, and the feed-track fallback when host-QC deps are absent. Full suite 534 green; typecheck + lint clean. (Real worktree-merge + suite-run verified in-app.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes host-driven QC: a FAIL no longer re-runs the whole suite on every board tick (a P4 gap). - runQc bug loop: on FAIL the engine increments a per-feature attempt count and files bug-task cards (seedBugCards), then WAITS — it re-runs QC only once all the feature's [BUG] cards are closed (openBugCards == 0), so workers fix → engine re-QCs → pass or new bugs. On PASS the attempt count clears. Bounded by sddpConfig.maxQcIterations. - escalation: attempt drives the tag — ≥3 ⇒ [ESCALATED], ≥maxQcIterations ⇒ [DEFERRED] — and the escalation message's urgency; deferred bug cards stay open so the loop pauses for manual review rather than spinning or auto-shipping failing QC. - index.ts deps: seedBugCards (parse the report's [BUG:…] lines → cards, else one generic; round-robin to workers; attempt→tag) + openBugCards (count open [BUG] cards for the feature). - docs: lifecycle now spec→…→tasks→ANALYZE→implement→qc; the host-engine paragraph rewritten for Analyze + checklist gate + host-driven QC (worktree) + the bug loop (replacing the "QC deferred" note). Tests: FAIL files bugs (attempt 1) then waits while open; re-runs once bugs closed → PASS writes .qc-passed + advances; attempt increments across cycles. Full suite 537 green; typecheck + lint clean. This finishes the sddp27 lifecycle port (P1 Analyze, P2 checklist gate, P3 sddp-config, P4 host QC, P5 bug loop) — all host-driven. (Worktree-merge + real suite-run verified in-app.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ries)
Deepens the QC bug loop to sddp27's full bug-task grammar:
[BUG:severity] [RECURRING?] [ESCALATED?] [DEFERRED?] {req?} [category] desc — file:line.
- index.ts seedBugCards: now builds titles via buildBugTitles + detects RECURRENCE — it computes the
signatures of every prior [BUG card for the feature (done cards from earlier attempts) and tags a
re-filed finding [RECURRING]; [ESCALATED] (attempt≥3) / [DEFERRED] (≥maxQcIterations) by attempt.
- sddpPipeline runQc: the qc-auditor prompt now emits one bug line per failure with a [category]
(test-failure/lint-error/security-vuln/coverage-gap/requirement-gap/pi-violation/runtime-error), so
seedBugCards parses rich findings instead of one generic card.
- docs: bug-task grammar + [RECURRING] note.
Package side (won-agent-core, gitignored): coordination/bugTasks.ts — pure bugSignature (normalized
recurrence key), bugCardTitle (canonical tag order, defaults severity, strips stale modifiers),
buildBugTitles (attempt→ESCALATED/DEFERRED + per-finding RECURRING) + barrel export. Tests (9):
signature normalization/equality, title grammar + stale-tag stripping, attempt thresholds, recurrence.
This is the last clearly-worth-it sddp27 item; preservation-enforcement / bootstrap / audit-logs /
manual-test.md remain deliberately deferred. Full suite 546 green; typecheck + lint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cipe Closes the "never run end-to-end" gap from both sides, without creating anything permanent. - src/main/__tests__/sddpPipelineE2e.test.ts (NEW): drives the REAL engine + REAL git.ts (prepareQcTree = actual `git worktree add` + merges) + REAL fs against a throwaway `git init` repo in os.tmpdir, with a scripted no-API provider that writes each step's artifact. Runs spec→clarify→…→analyze→implement→host-QC and asserts: every milestone advances, analysis-report.md is critical:0, .qc-passed lands, the two implement branches really merge in the QC tree (conflicts: []), and the QC worktree is torn down — then rmSync's the temp dir. A FAIL variant asserts QC holds, no .qc-passed, and a [BUG] card is filed. (Exercises the git/fs plumbing every other test fakes; requires git on PATH; ~2s.) - docs/sddp-smoke.md (NEW): the disposable in-app sandbox recipe for the LIVE path the test can't cover (real DeepSeek desk) — isolate harnessHome to a temp dir (config:changeHome 'fresh'), register a throwaway repo, SDDP+autopilot, run one tiny feature, watch the pills, tear down — plus a failure-mode table (needs-credentials, no owner, analyze CRITICAL, checklist hold, QC conflict, bug loop). Test-only + a doc; no production code change. Full suite 548 green; typecheck + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…path) Replaces the 2-case happy/fail smoke with a single suite that drives EVERY engine path against REAL git + REAL fs (real prepareQcTree/mergeBranch + real analyzeCoverage + the real bug-task grammar), each in its own throwaway temp repo: 1. happy → integrate-to-trunk (full run to .qc-passed, then real mergeBranch each impl branch into main) 2. merge CONFLICT (two branches edit the same line → prepareQcTree conflicts → QC escalates, no marker) 3. full bug loop (FAIL→wait-while-open→close→re-QC FAIL tagged [RECURRING]→close→re-QC PASS→.qc-passed) 4. Analyze CRITICAL → recover (uncovered FR-002 holds critical:1; fix tasks.md + delete report → clean) 5. optional-step skip (research/data-model/contracts/adrs advance with no artifact) 6. Clarify autopilot OFF (requirements-scanner asks + holds; operator-advance continues) 7-9. per-step control: paused (no run), manual (skip→advance on artifact), stopped (abort in-flight run) 10. no usable owner → escalate; with a planner → auto-assign the epic 11. idempotent (pre-existing .qc-passed advances without building a tree) 12. in-flight lock (two concurrent advances → exactly one spawn) The factory (setupE2E) is generalized with mutable state flags (qcPass/autopilot/uncovered/skipOptional/ conflict/ownerUsable/plannerDesk/gate) + helpers (setControl, closeBugCards, operatorAdvance, drive). Honest boundary unchanged: the LIVE layer (model/worker/cwd-override/IPC/UI/god-epic/integrate-desk) is not automatable — docs/sddp-smoke.md remains its only coverage; integrate here is a simulated mergeBranch. Runtime: the 12 real-git tests add ~8s (each shells out to git). Full suite 558 green; typecheck + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ly the model faked) The exhaustive E2E mocked each sub-agent as a coarse black box (name -> write a placeholder file): it proved the engine + real git/fs but never ran the real sub-agent runner, the real toolkit, the cwd-override, or the deny-list together. Those layers were unit-tested in isolation but never COMPOSED with the engine, because the spawn glue was electron-coupled in index.ts and hardcoded the worker transport. Phase A — extract the spawn glue (behavior-identical). The body of spawnSubAgentFor couples to only three host edges: the electron worker transport, executeNativeToolFor, and the telemetry cost rollup; everything else is pure logic over already-electron-free modules. Move that body into a new electron-free runtime/subAgentExecutor.ts (makeSpawnSubAgent(deps)) with those three edges injected, and rewire index.ts to construct it. Production behavior is unchanged (full suite + typecheck verify parity). Phase B — sddpFullStack.test.ts. Wire the engine's spawnSubAgent/spawnSubAgentInTree to makeSpawnSubAgent over a SCRIPTED fake transport (the only fake — it stands in for the LLM token/tool stream) + the real executeAgentTool over a throwaway git repo. So each sub-agent's tool calls actually execute: spec/plan/tasks are authored by a real write_file, the checklist is flipped by a real edit_file, and the qc-auditor's bash + write_file run with the cwd OVERRIDDEN into the merged QC worktree (process.cwd() proves it) while qc-report.md redirects to the shared specs/. Three cases: happy path, deny-list refusal (spawn_subagent/hive_integrate refused before the toolkit), and a QC FAIL -> bug card -> re-run PASS. Phase C — test display. Add a `test:e2e` verbose script (one check-line per case) and an SDDP_E2E_TRACE env gate that logs each milestone as the drive loop advances. The live model's prompts/outputs remain un-automatable (sandbox only — see docs/sddp-smoke.md); this fakes the model's stream, not the model. 561 tests pass; typecheck clean; lint 0 errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ovider Deepen sddpFullStack.test.ts so the only thing faked is the provider's token/tool turns. The transport now runs the real `runAgentLoop` in-process (a programmable stub provider replays each specialist's turns), so the full chain executes for real: engine -> makeSpawnSubAgent (deny-list + cwd-override) -> runOneShotSubAgent -> NativeAgentWorker (toolRequest/toolResult round-trip) -> real runAgentLoop (turn/hop caps, request->tool->result cycle, usage rollup, stop) -> real executeAgentTool -> real git/fs. Replaces the prior hand-emitted tool stream. Closes the four coverage gaps the prior version left: - Read path: each specialist now READS before it authors (read_file/list_dir/grep) per its subAgents.ts prompt, so the toolkit's read path + specs/-redirect-on-read run composed, not just in isolation. - Real loop: the loop GENERATES each sub-agent's output stream (asserted via its turn-start/tool-start/tool-end/stop events + the token-usage rollup), instead of the test hand-emitting it. - Branches: every meaningful engine path now runs through the real stack — happy, deny-list, QC-fail, merge-conflict, recurring-bug ([RECURRING]), analyze-CRITICAL, optional-skip, clarify-autopilot-OFF (requirements-scanner). - hive/web/memory: a focused case round-trips hive_read_memory / hive_list_tasks / hive_feature_status / write_memory / web_search through the real toolkit. 9 cases (was 3). Still test-only; won-agent-core + production code untouched. The live model's prompts/outputs stay un-automatable (sandbox only — docs/sddp-smoke.md); the stub stands in at the ProviderCall seam, the realistic in-process boundary. 567 tests pass; typecheck clean; lint 0 errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
An honest audit of sddpFullStack.test.ts found six remaining gaps; three are closeable in vitest, three are inherently sandbox-only. Close the closeable three and write down the ceiling. Closeable (now covered, composed through the real loop + real toolkit): - Real tool FAILURES + self-correction: a sub-agent hits genuine execution failures (read a missing file, bash non-zero, write outside the sandbox) — distinct from a deny-list refusal — and the real loop feeds each back so a later hop recovers and really writes. Previously only the success path + deny-list were composed. - The REAL DeepSeek adapter at the wire level: one case swaps the scripted stub for makeDeepseekAdapter driven by a mocked fetch/SSE stream, so request-building + SSE parsing run COMPOSED with the loop -> real toolkit -> fs (only the socket is faked). Adds a providerCall-injection refactor to realLoopTransport (+ opts.providerFor). - The integrator's real hive_integrate merge: an integrator-role desk calls hive_integrate with a real `integrate` dep that runs a real `git merge` into trunk after .qc-passed; the files land on trunk, and a non-integrator is refused by the gate. Previously only the deny-list refusal of hive_integrate was tested. Documented in docs/sddp-smoke.md the three inherently sandbox-only gaps (the ceiling): the electron utilityProcess worker, the god-creates-epic + reviewer/integrator ping choreography, and the fact that the stub ignores each sub-agent's systemPrompt — so only a live model tests whether the prompts actually elicit the right behavior. Also refreshed section 1 to describe both suites + `npm run test:e2e`. 12 full-stack cases (was 9); 570 tests pass; typecheck clean; lint 0 errors. Test + docs only; won-agent-core + production code untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A fourth "are you testing everything?" prompted a deeper skeptical audit; it found four more composable gaps beyond the documented sandbox-only ceiling. Close them all (test-only) and write down the complete coverage map so nothing is silently uncovered. New composed cases in sddpFullStack.test.ts (now 16): - Multiple tool-uses in ONE provider turn — a real model parallelizes tool calls; the loop now provably executes every tool from a single turn (was: one tool per turn). - The REAL Minimax adapter at the wire level — driven over a mocked Anthropic-style SSE stream (tool_use assembly + final text), composed with loop -> toolkit -> fs. Previously only DeepSeek was composed; Minimax was unit-tested only. - Reasoning/thinking — the DeepSeek case now streams reasoning_content and asserts a thinking event surfaces through the loop. - EVERY registered sub-agent (all 20) — data-driven over SUB_AGENT_NAMES: each spawns and its write_file is allowed/denied per roleAuthorsCode(spec.roles), so the 5 never previously spawned (adversarial-scanner/checklist-reader/task-tracker/ configuration-auditor/developer) + the role->deny-list gate are covered for the whole registry. - A PERSISTENT desk (new describe) — a NativeAgentWorker driven across TWO turns via the real loop with a block:true drain continuation; tools route to the real toolkit and memory written in turn 1 is read in turn 2. Closes the persistent-loop + drain + cross-turn-memory gaps. (realLoopTransport gained an optional drain() param.) docs/sddp-smoke.md: replaced the prose with a full COVERAGE MATRIX (every layer -> COVERED-COMPOSED / UNIT-ONLY / SANDBOX-ONLY with where/why), and an explicit irreducible ceiling: the electron utilityProcess worker + NativeRuntime.spawn lifecycle, the god-creates-epic + reviewer/integrator ping choreography, and prompt efficacy (the stub ignores systemPrompt — only a live model tests whether prompts work). Honest bottom line: coverage is asymptotic, but every gap that reflects real production behavior and is composable in vitest is now closed; the only remaining items are the three documented sandbox-only ones + a few unit-tested-elsewhere internals. 574 tests pass; typecheck clean; lint 0 errors. Test + docs only; won-agent-core + production code untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…an + ground the feature steps
SDDP mode drove only the per-feature specify→qc lifecycle from a hand-seeded epic
on a bare repo. Make it pick up an ALREADY-bootstrapped repo (init + PRD + system
design + devops + project-plan done) and run the feature workflow hands-off.
- Bootstrap-aware buildStepInput (sddpPipeline.ts): the grounded steps (spec / research
/ data-model / contracts / adrs / plan) now tell their sub-agent to read the project
docs — specs/prd.md, specs/sad.md, specs/dod.md, specs/project-plan.md,
project-instructions.md — so the feature artifacts are grounded in the bootstrap docs
instead of ignoring them. Additive prose; locations per .claude/rules/sddp-bootstrap.md.
- Auto-derive epics (the before-Specify step): a pure parser (won-agent-core
projectPlan.ts: parseProjectPlanEpics / featureFolderForEpic / epicCardDescription)
turns the plan's `- [ ] E### [P#] [CAT] {SAD:..}{PRD:..} <title> — <detail>` checklist
into ParsedEpics; SddpPipeline.seedFeaturesFromPlan(repo) reads specs/project-plan.md
and seeds one feature epic card (defaultMilestones + derived 00001-slug feature folder
+ a description carrying the PRD/SAD refs) per pending epic, idempotently (skips ones
already carded by E### id). New injected deps projectPlanText/seedFeatureEpic mirror
the seedImplementCards seam. This CONSUMES a finished plan — it does not re-run
projectplan.
- Host wiring (index.ts): implement the deps over real fs + the hive, and a thin,
idempotent auto-seed trigger — on SDDP enable (config:update), at startup (whenReady),
and on each board tick — over every registered repo. Gated on SDDP mode; the god can
still create epics too.
- End-to-end proof (sddpFullStack.test.ts, case 17): a temp repo bootstrapped with
prd/sad/dod/project-plan/project-instructions (committed, no feature work) →
seedFeaturesFromPlan auto-creates the E001 epic (feature 00001-provider-runtime-and-
event-bus, refs in the description) → specify→qc runs to .qc-passed under autopilot
with ZERO escalations, and the sub-agents REALLY read specs/prd.md + specs/sad.md via
the real toolkit (consumption proven, not just presence). Re-seeding is idempotent.
The harness is now feature-folder-dynamic, so the prior 16 cases still resolve to
'00001' and stay green.
582 tests pass (incl. 7 new projectPlan unit tests); typecheck clean; lint 0 errors.
Additive + SDDP-gated; the won-agent-core parser lives in its own repo (not committed
here); only main-side files staged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sekeeping - README: "What this fork adds" high-level section - FORK-FEATURES.md: detailed per-feature catalog of fork additions vs upstream - LICENSE: add James Han copyright - .gitignore / .claude/settings.json: ignore local reference copy + perms Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Type of change
Screenshots / clips
Checklist
npm run typecheckpasses (the de-facto CI gate).npm run buildsucceeds.DESIGN.md/tokens.ts— no ad-hoc colors, spacing, or fonts.ATTRIBUTION.md.