* fix(#2176): ground the Antigravity reviewer in the repo under review
- capability-probe --add-dir (mirrors the Codex bypass-flag probe) and pass
the repo root on both invocation arms
- anchor _AGY_PROMPT to the absolute repo root; mandate a
REVIEWED-WITHOUT-REPO-ACCESS self-report when the repo is unreadable
- stamp a [reviewed-without-repo-access] marker on self-reported or
scratch-anchored output; Consensus Summary down-weights marked reviews
- apply the same absolute-root anchor to the cursor-agent prompt (AC5)
Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg
* docs(#2176): changeset fragment for PR #2184
Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg
* fix(#2176): review fixes — size baseline, cursor root anchor, anchored blind tells
- regenerate tests/workflow-size-baseline.json for review.md's growth
- cursor anchor uses git rev-parse --show-toplevel (bare pwd resolved the
wrong root from a repo subdirectory)
- blind-review tells anchored: self-report to the first lines of output,
scratch tell to a workspace-declaration phrasing — a grounded review
quoting either string is no longer mis-stamped
Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg
* fix(#2176): round-2 review fixes — scratch-tell bridge, behavioral test, changeset
Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg
* test: regenerate golden-install-parity fixtures for the review.md change
Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg
* test(#2176): pass the transcript path to bash with forward slashes
The behavioral detection test substitutes a mkdtemp path into the bash
compound; on Windows runners that path contains backslashes, which bash
strips, so the transcript is never found and the first assertion fails
(windows-latest/24 lane). Git Bash accepts D:/-style paths.
Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg
* fix(#2176): use /gsd:review namespace syntax in workflow comment
The slash-command namespace invariant (#3443) bans retired /gsd-<cmd>
references in Claude-facing sources; a cursor-anchor comment used
/gsd-review. Size baseline + golden fixtures regenerated for the byte
change.
Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg
* test(#2176): derive the POSIX path via path.sep, not a hardcoded separator
Review finding: out.replaceAll('\\', '/') hardcodes both separators;
use the separator-safe out.split(path.sep).join(path.posix.sep) idiom.
Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg
* test(#2176): use the merged toPosixPath seam for the bash path
Per maintainer note: #2247's shell-command-projection now centralizes
running-OS → POSIX path conversion; import it instead of the inline
split/join idiom.
Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg
* fix(execute-phase): honor gate="blocking-human" in auto-mode checkpoint handling
The package-legitimacy gate (#2827) spans two layers. gsd-executor refuses to
auto-approve a gate="blocking-human" checkpoint and escalates it so a human can
vet the package. execute-phase's checkpoint_handling step then dispatched purely
on checkpoint *type* and never read gate -- so under --auto/--chain it
auto-approved the checkpoint the executor had just refused to auto-approve.
Net effect: the slopsquatting defence was inert in exactly the unattended mode
where it matters. An [ASSUMED]/[SUS] package reached install with no human ever
seeing the prompt.
- gsd-core/workflows/execute-phase.md: carve out gate="blocking-human" (and the
package-legitimacy what-built markers) ahead of every auto-mode branch.
- gsd-core/references/checkpoints.md: document the gate attribute and its two
values. blocking-human previously appeared nowhere outside gsd-executor.md,
so no planner had a documented way to author a non-auto-approvable checkpoint.
- tests/package-legitimacy-gate.test.cjs: the existing regression test asserted
the executor half only, which is why it stayed green while the gate was open.
Now asserts the orchestrator half too.
* chore(changeset): link to issue #2107
* chore(changeset): backfill PR number 2113
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JNR8m2pv5U7ubn4iiXVrMa
* test(#2107): refresh golden-install-parity hashes for edited gsd-core files
The golden fixtures pin content hashes for gsd-core/references/checkpoints.md
and gsd-core/workflows/execute-phase.md, both edited by this fix. Regenerated
via UPDATE_GOLDEN=1; only those two keys change across all 17 runtime fixtures.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JNR8m2pv5U7ubn4iiXVrMa
* fix(#2107): keep the carve-out inside the ADR-857 host-loop budget
The ADR-857 phase-6 ratchet pins execute-phase.md below 93600 LF bytes so
optional-feature logic keeps migrating out of the host loop. The carve-out
first landed 623 bytes over that ceiling.
Move the two-layer rationale (why gsd-executor escalates these checkpoints)
into references/checkpoints.md, where the gate is now documented, and reduce
the workflow to the operative rule. execute-phase.md is 93589 bytes, under
the ceiling; the gate token and both <what-built> marker strings are kept
because the orchestrator matches on them.
Refresh the two baselines the edit invalidates: golden-install-parity
fixtures (only the checkpoints.md and execute-phase.md hashes move) and
workflow-size-baseline.json (one line). The ADR-857 ceiling itself is
untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JNR8m2pv5U7ubn4iiXVrMa
* fix(#2107): executor honors blocking-human on the decision branch + gate transport
Review found the fix incomplete one layer down. Two executor-layer gaps:
1. Blocker — agents/gsd-executor.md auto-mode dispatch gated
checkpoint:human-verify on gate="blocking-human" but the checkpoint:decision
branch below auto-selected the first option with no gate check. The executor
resolves a decision itself (auto-selects and continues) without returning it,
so the orchestrator carve-out never runs for it. A planner following the new
checkpoints.md rule 6 ("gate a decision whose default would be wrong to
assume") would have it silently auto-selected under --auto/--chain — the exact
#2107 harm, one checkpoint type over. The decision branch now STOPs and
returns for an explicit human decision when gate="blocking-human".
2. Major (transport) — checkpoint_return_format carried no field conveying the
gate to the freshly-spawned orchestrator, so recognition of the proactive
pre-install checkpoint rested on freeform prose. Added a **Gate:** field to
the return format and re-pointed the execute-phase carve-out at it
("If the returned Gate: is blocking-human"). Net byte-negative: execute-phase.md
drops 93589 -> 93583, widening ADR-857 headroom from 11 to 17 bytes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(#2107): cover decision carve-out + gate transport, de-vacuum conditional tests
- New: 'auto mode does not auto-select a blocking-human decision checkpoint'
asserts the executor decision branch STOPs on blocking-human. Verified red on
the pre-fix executor (2 fail), green with the fix (27 pass).
- New: 'checkpoint_return_format transports the gate ...' asserts the **Gate:**
field carries blocking-human across the executor->orchestrator boundary.
- New: 'auto-select rule for decision is conditional' — orchestrator-side mirror
of the human-verify conditional test, for the execute-phase decision branch.
- Fix vacuous test: both conditional tests now assert the anchor matched
(length > 0) before iterating, so anchor drift can no longer pass with zero
assertions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(#2107): refresh golden + size baselines for executor + execute-phase edits
Regenerated via UPDATE_GOLDEN=1 and update-size-baseline.cjs. Only the
gsd-executor.md and gsd-core/workflows/execute-phase.md hashes move across the
runtime fixtures (35 ins / 35 del, no keys added or removed); checkpoints.md is
unchanged this round. Size baselines: gsd-executor.md 43607 -> 43973,
execute-phase.md 93589 -> 93583 (still under the ADR-857 ceiling).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
* feat(#2161): opt-in absolute token count on the statusline context meter
New statusline.show_context_tokens config (default false). When enabled,
the context meter shows the absolute token total after the percentage,
e.g. "████░░░░░░ 46% (156k)" — summing input, cache-creation, cache-read,
and output tokens from context_window.current_usage (matching /context).
Default output is byte-for-byte unchanged when the flag is absent or
false. The .planning config is now read once per render and shared with
the last-command/position block instead of being re-read.
Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg
* docs(#2161): changeset fragment for PR #2174
* fix(#2161): review fixes — k-to-M threshold, boundary tests, changeset format
- formatTokens promotes to the M branch when k-rounding reaches 1000
(999,500-999,999 rendered "1000k" instead of "1.0M")
- boundary tests at 999499/999500/999999/1000000/1000001
- Number() guards on the four usage fields (silent string-concat gap)
- changeset body ends with the (#2161) citation per house convention
Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg
* fix(#2161): round-2 review fixes — config-set coverage, precision claim, exports style
- config-set accept/reject tests for statusline.show_context_tokens
(mirrors the post-planning-gaps precedent the issue scope names)
- changeset + docs no longer claim parity with /context: the suffix sums
four fields while the meter %% derives from used_percentage (three), so
the figures can diverge slightly
- module.exports one entry per line
Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg
* test: regenerate golden-install-parity fixtures for the statusline hook change
Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg
---------
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
* fix(#2206): strip trailing slashes in isGitIgnored to avoid CRLF check-ignore quirk
isGitIgnored was called with a trailing slash (`.planning/`) in config-loader.
git check-ignore has a longstanding quirk: a CRLF .gitignore with blank lines
falsely reports any path WITH a trailing slash as ignored. This silently set
commit_docs=false on Windows repos (where CRLF .gitignore is the norm), skipping
all planning-doc commits. Normalize trailing slashes inside isGitIgnored so every
call site is protected.
Closes#2206
* docs(#2206): add changeset fragment
* docs(#2206): backfill PR number
* fix(#2203): traceability parser matches REQ-IDs in any column, not just the first
The traceability table-row parser required the REQ-ID in the first column
(`^| REQ-ID |`). A table that leads with a status column (e.g. `| ☐ | REQ-01 |`) matched zero rows, so phase complete warned every body REQ-ID was missing.
Match REQ-IDs in any pipe-delimited cell (drop the ^ anchor).
Closes#2203
* docs(#2203): add changeset fragment
* docs(#2203): backfill PR number
* fix(#2202): preserve unknown frontmatter keys in syncStateFrontmatter
syncStateFrontmatter rebuilds frontmatter from a fixed schema, dropping any
custom/unknown key on every mutating verb. Before reconstruction, merge any
existing frontmatter key the schema does not own. Schema keys still win.
Closes#2202
* docs(#2202): add changeset fragment
* docs(#2202): backfill PR number
* fix(#2202): add regression test + remove redundant type assertion
- tests/state.test.cjs: behavioral regression test asserting custom/unknown
STATE.md frontmatter keys survive a mutating verb (they were silently dropped
before the syncStateFrontmatter carry-forward).
- src/state.cts: drop the unnecessary `as Record<string, unknown>` assertion
that tripped @typescript-eslint/no-unnecessary-type-assertion (the lint-tests
gate failure).
Refs #2202
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(2119): single SECURITY.md writer — auditor is return-only
The gsd-security-auditor held Write/Edit and was instructed to write
SECURITY.md (no <N>- prefix, no template frontmatter), while the
orchestrator's Step 6 also wrote the correct padded <N>-SECURITY.md
from templates/SECURITY.md. Two writers, two naming conventions, two
shapes — the auditor's unprefixed file was invisible to the workflow's
*-SECURITY.md glob detector and unparseable for the threats_open gate.
Fix (option 1 from the issue): make the auditor return-only.
- Remove Write/Edit from auditor's tools
- Rewrite all 'Write SECURITY.md' instructions to 'Return structured
verdict' with threats_open count
- Add explicit constraint in workflow Step 5 spawn prompt
- Update existing test (was asserting Write in tools — now asserts absence)
- Add new regression test for single-writer contract
- Update docs/AGENTS.md stale Tools/Produces rows
- Regenerate golden fixtures + agent size baseline
* docs(changeset): backfill PR number (#2154)
* chore(#2119): regenerate pi/qwen golden fixtures after next merge
The single-writer change edits gsd-core/workflows/secure-phase.md and
agents/gsd-security-auditor.md; pi.json (added on next) and qwen.json (merge
straggler) were the only runtime fixtures still holding pre-change hashes for
those files. All other runtimes already reflect the change. Regenerated via
the sanctioned gen-golden-install-parity script.
* merge origin/next — regenerate goldens + baseline for merged state
* fix slash-command syntax: /gsd-secure-phase → /gsd:secure-phase (#2154 CI fix)
* fix(#2200): scope phase-complete roadmap writes to the current milestone
runPhaseCompleteTransaction's roadmap mutators ran unanchored / un-milestone-
scoped / first-match over the whole ROADMAP: the phase-checkbox flip could check
a bullet inside a backticked prose literal or an earlier Backlog entry instead of
the closing phase's bullet (a transposition), and the Plans-count writer could
bind to a same-numbered phase in a shipped milestone.
- Add currentMilestoneRawRanges(content, cwd) to roadmap-parser.cts: raw
[start,end) offsets of the active milestone's region(s) (primary section + the
optional Phase Details section), mirroring extractCurrentMilestone's selection.
- Line-anchor the phase checkbox pattern (^ + m flag) so an inline / backticked
prose literal cannot match.
- Apply the phase-checkbox flip, the Plans-count write, and the per-plan checkbox
flips ONLY within the current milestone window(s) via a mutateMilestonePhase
helper (splice later windows first so offsets stay stable). Fall back to whole-
content mutation when there is no versioned active milestone (prior behaviour).
The Progress-table writer stays as-is (already scoped to ## Progress, #2012).
Closes#2200
* docs(#2200): add changeset fragment
* docs(#2200): backfill PR number in changeset fragment
* fix(2112): scope commit to --files pathspec, not entire index
cmdCommit/cmdCommitToSubrepo/cmdPrSubrepo staged exactly the files
named in --files but then ran a bare 'git commit' with no pathspec,
absorbing anything else in the index into a commit whose message
described only the named files (#2112).
Fix: append '-- ...stagedPaths' to the commit args when the caller
declared a scope. Three guards are load-bearing:
- stagedPaths (not filesToStage) excludes skipped missing files (#2014)
- explicitFiles gate keeps the default .planning/ path byte-identical
- MERGE_HEAD check via 'git rev-parse' falls back to bare commit during merge
- --amend is left without pathspec (different operation)
cmdPrSubrepo pathspec uses changedFiles (old+new for renames) so the
full rename is captured atomically.
Also fixes workflow markdown in spec-phase.md and add-tests.md.
All-files-missing now short-circuits to nothing_to_commit instead of
absorbing the entire index under a message describing files that
were not committed.
* docs(changeset): backfill PR number (#2148)
* test: update golden-install-parity fixtures for workflow markdown changes (#2112)
* test: update golden fixtures + workflow baselines for #2112 changes
- claude-local.json golden fixture (now generated via gen script)
- workflow-size-baseline.json (add-tests.md +16, spec-phase.md +42 bytes)
- Extended gen-golden-install-parity-zcode.cjs to also regenerate the
claude local-layout fixture
* fix(2118): honor --dry-run in milestone complete with zero-mutation preview
milestone complete treated --dry-run as a no-op: the flag was neither
parsed nor rejected, so a caller who expected a preview instead
triggered the full destructive mutation (archive phases → move audit
artifacts → rewrite STATE.md) with no way to back out.
Fix (option 2 from the issue): add dryRun to MilestoneCompleteOptions,
parse --dry-run in the dispatcher, and return a JSON preview plan
(would_archive, would_update) after the read-only stats gathering but
before any mutations. Also gated platformEnsureDir on !dryRun so the
archive directory is not created during preview.
3 regression tests: no-mutation happy path, --no-archive-phases combo,
and --force bypass combo.
* docs(changeset): backfill PR number (#2155)
* chore(#2118): regenerate pi/qwen golden fixtures after next merge
The milestone --dry-run fix changes gsd-core/bin/gsd-tools.cjs; qwen.json
(missed at authoring) and pi.json (added on next, never carried the fix)
were the only two runtime fixtures still holding the pre-fix hash. All
other runtimes already reflect the change. Regenerated via the sanctioned
gen-golden-install-parity script.
* fix(#2118): surface accomplishments in dry-run preview; fix --dry-run --raw
Orthogonal review findings on the --dry-run preview:
- The preview omitted the already-computed accomplishments (the primary
MILESTONES.md content a real run writes); surface it as a top-level field,
mirroring the real-run result.
- `--dry-run --raw` discarded the structured payload and printed the literal
string "dry-run"; drop the raw-value arg so --raw emits the full preview
JSON, matching the real-run output() call.
Adds tests: --dry-run --raw is parseable JSON, preview includes accomplishments,
and --dry-run --force is proven zero-mutation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#2201): accept --phase N flag in the phase verb family (complete, list-plans)
The phase family router treated the first positional (args[2]) as the phase
number, so `phase complete --phase 12` passed the literal '--phase' as the phase
→ 'Phase --phase not found'. The state family already accepted --phase N. Now
complete and list-plans accept --phase N (and --phase=N) as well as the bare
positional; unrecognized flags yield a usage error naming the accepted form.
Closes#2201
* docs(#2201): add changeset fragment
* docs(#2201): backfill PR number
* test(#2199): cover bullet/em-dash ROADMAP phase resolution + milestone count
Adds the bullet-only ROADMAP fixture the suite lacked: an all-bullet em-dash
ROADMAP resolves each phase (no Phase null), colon/en-dash/hyphen bullet
separators all resolve, mixed heading + bullet forms coexist, and the milestone
phase-count counts bullet-form phases instead of collapsing to zero.
* fix(#2199): accept bullet/em-dash phase entries in roadmap lookup + milestone filter
Roadmap phase lookup (findRoadmapPhaseInContent) matched only ATX headings
against a colon-required pattern, so a bullet/checkbox entry like
`- [ ] **Phase N — name**` — which the bundled roadmapper emits in bullet-house-
style ROADMAPs — resolved found:false and `Phase null` was written into STATE.md.
The milestone phase-filter built its phase set from headings only, so a bullet-
only ROADMAP collapsed to a zero-count pass-all filter and progress denominators
broke.
- Add a shared bullet-phase-line pattern (separator: em-dash/en-dash/hyphen/colon).
- findRoadmapPhaseInContent: on a heading-match miss, fall back to the bullet line
for the requested phase; return found:true with the captured name.
- getMilestonePhaseFilter: also scan bullet lines into the milestone phase set.
Closes#2199
* docs(#2199): add changeset fragment
* fix(#2199): bullet phase lookup as last resort + consolidate test (review)
Two corrections to the initial fix:
1. Regression — the bullet fallback inside findRoadmapPhaseInContent was too
eager: it returned a bullet match from the scoped (current-milestone) content
before the caller tried the full-content heading path, so a phase whose
Requirements live in a Phase Details heading (after the active-milestone
section) got a bullet-line section with no Requirements → phase_req_ids null
(broke 3 init tests). Restructure: findRoadmapPhaseInContent is heading-only
again; a separate findRoadmapBulletPhaseInContent runs in getRoadmapPhaseInternal
ONLY after scoped + full heading lookup fails, so a heading with a Requirements
section always wins.
2. lint-test-file-count — the standalone fix-2199-roadmap-bullet-phase.test.cjs
collided with the over-cap 'roadmap' module (FAIL_NOVEL_FILES). Consolidate
the regression into the existing tests/roadmap-parser.test.cjs (its natural
home, under the 2-file cap).
* test(#2199): assert heading-in-full beats bullet-in-scoped (review L3)
The exact first-attempt regression: a phase has a bullet in the active-milestone
scope but its heading (carrying Requirements) lives in a Phase Details section
outside that scope. Pin that the heading section wins over the bullet line so
req_ids resolve and the eager-bullet bug cannot return.
* docs(#2199): backfill PR number in changeset fragment
The .changeset fragment used type "Documentation", which is not in the
allowed Added|Changed|Deprecated|Removed|Fixed|Security set — corrected
to Fixed (this fragment describes a bug fix). Regenerated
skills/gsd-surface/SKILL.md via gen:plugin-skills so it reflects the
resolvable require-path fix already applied to commands/gsd/surface.md,
clearing the stale-generated lint failure.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Four require() examples in commands/gsd/surface.md used bare
'gsd-core/...' specifiers that Node cannot resolve (wrong package
name + runtime-mirror layout off module path). Now derives the path
from runtimeConfigDir. Also fixes reinstall hint from
'npm i -g gsd-core' to 'npm i -g @opengsd/gsd-core'.
scanEntropyAnomalies + shannonEntropy were dead exports with zero
production callers — the live hooks (gsd-prompt-guard.js,
gsd-read-injection-scanner.js) inline their own pattern subsets for
hook independence and never called these functions.
Changes:
- Remove scanEntropyAnomalies + shannonEntropy from src/security.cts
- Remove scanEntropyAnomalies test block from tests/security.test.cjs
- Correct REQ-SCAN-INJ-02/-03 in FEATURES.md (EN/zh-CN/ja-JP) to
describe what actually runs live (injection patterns, invisible
Unicode) vs CI-only (base64-decode, codebase scan)
- Correct docs/security/baseline.md §2.4 to clarify live hooks inline
patterns, not import from security.cts
- Add regression test asserting the corrected contract
- scanForInjection retained: it serves as the CI codebase-scanner engine
VS Code is a net-new EoS runtime that — unlike every prior migration — is NOT
CLI-installed (Marketplace/VSIX extension). It has zero runtime==='vscode'
branches in bin/install.js and stays that way (regression-guarded); it is driven
entirely through the negotiated imperative Host-Integration adapter.
Registry + validator (the hard part):
- capabilities/vscode/capability.json (role:runtime): full hostIntegration block
(imperative / palette / active vscode.lm model / engine hook bus /
sandboxed-storage / mcp transport / sandboxed-web runtime; dispatch nested,
maxDepth 5 per VS Code's documented subagent depth).
- capability-validator.cjs extended so a role:runtime capability can legitimately
declare "extension-distributed, no config directory": new configHome.kind:'none'
+ installSurface:'none' (+ GATE-A pairing + the parity maps), with localConfigDir
and configHome.name made conditional on kind!=='none'. All 18 runtimes still
validate; getDirName returns a distinct sentinel (not '.claude') for a no-config
runtime.
- The add-a-registry-runtime tax: NON_INSTALLABLE_RUNTIMES exemption in the
runtime-flags drift guard, vscode added to global-config-home SPECIAL_CASED,
EXPECTED_PROFILES.vscode='ide', and the config-adapter/derivation/pin-count
guards updated. No golden-install fixture, model-catalog, or CONFIGURATION rows
(vscode never enters allRuntimes).
Dispatch + extension surface:
- Fixed vscode/extension.js's createHub()-no-args bug (every dispatch was
UnknownCommand, masked by a vacuous reachability test) — now reuses the shared
dispatchGsdCommand subprocess-shim (Node/desktop); the reachability test is
tightened to assert real dispatch.
- Promoted the #1933 host binding to a shipped vscode/host-binding.js; activate()
now composes the model/hookBus/stateIO seams through it. Corrected the model
seam to VS Code's real API (vscode.lm.selectChatModels() -> model.sendRequest();
vscode.lm.sendRequest does not exist) so the binding actually composes on real
desktop VS Code instead of throwing.
- New vscode/browser.js Web Extension entry with ZERO Node APIs (the engine's
config/capability loading is Node-bound, so the web entry registers the surface
and directs full dispatch to the native MCP server — honestly documented).
- UPGRADE 1: GSD skills as native Language Model Tools (contributes.languageModelTools
+ vscode.lm.registerTool), invoke() dispatching through the hub.
- UPGRADE 2: native subagent dispatch wired onto #runSubagent /
chat.subagents.allowInvocationsFromSubagents (fail-soft on API availability,
maxDepth 5 enforced).
- vscode/package.json: browser entry, engines.vscode ^1.105, chatParticipants +
languageModelTools contributions; fixed a stale activationPoints->activationEvents
manifest key. Added "vscode" to the package files array.
Docs (## vscode matrix section) + changeset (Added).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
audit-milestone's Nyquist scan classified a phase from `nyquist_compliant`
alone, so a phase seeded by plan-phase but never run through validate-phase
read PARTIAL — identical to a phase that validated and genuinely failed. The
template's `status` field could discriminate the two, but no workflow ever
promoted it off `draft`, so it was dead.
Make `status` live and read it:
- validate-phase.md §6: set `status: validated` in both the create (State B)
and update (State A) VALIDATION.md paths.
- audit-milestone.md §5.5: parse `status`; add a distinct NOT-VALIDATED bucket
keyed on `status: draft`, gate COMPLIANT/PARTIAL on `status: validated`, and
report `not_validated_phases` in the audit YAML.
- VALIDATION.md template: document the draft → validated lifecycle.
Tests & generated artifacts:
- Regression test folded into policy-138 (owning workflow-contract file);
fail-first verified vs origin/next (0 matches pre-fix).
- Regenerate golden-install-parity fixtures cleanly: adds the previously-missed
qwen.json and removes a contaminated `settings.local.json` entry that had
leaked into claude-local.json (the harness excludes hook-config files).
- Correct a stale validate-phase.md workflow-size-baseline entry.
Closes#2117
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Net-new EoS/pi installable runtime — purely additive (no prior runtime==='pi'
branches). pi is a bun-runtime programmatic-CLI whose /gsd command is registered
by a native ExtensionAPI extension and dispatches through the embedded engine.
Stage 1 (install plumbing):
- capabilities/pi/capability.json: full hostIntegration descriptor (imperative /
slash-programmatic / active-model / native-extension / bun) + hostBehaviors
{nativePlugin, pluginOnlyInstall}.
- --pi flag + interactive-menu renumber (All 17->18); pi added to RUNTIME_FLAG_IDS,
RUNTIME_LABELS, RUNTIME_META, allRuntimes/runtimeMap, model-catalog defaults.
- Install mirrors OpenCode: pi installs the gsd.cjs extension + the shared engine
payload (gsd-core + scripts + config markers) + the shared hooks bundle (spawned
by the extension at lifecycle events, like OpenCode's plugin). pluginOnlyInstall
EXCLUDES declarative command/agent/skill markdown, which pi has no host-read
surface for (its /gsd is programmatic). _installNativePluginIfDeclared (extracted
from the opencode-family path) copies pi/gsd.cjs -> ~/.pi/agent/extensions/gsd.cjs
(global) / .pi/extensions/ (local). pi added to package.json files.
- Golden: new pi.json (320 files: extension + engine + 27-file hooks bundle, no
markdown); the 16 other fixtures + claude-local change only by the shared
model-catalog hash line.
Stage 2 (real dispatch + upgrades):
- Shared dispatchGsdCommand() (shell-command-projection): bounded, no-throw
subprocess-shim to gsd-tools.cjs (the only full-surface dispatch path; no
in-process full-hub factory exists). Fixes pi/gsd.cjs's createHub()-no-args bug
(every dispatch was UnknownCommand) AND the identical bug in mcp-server.cts's
gsd_invoke_command, which a vacuous unknown-family-only test had masked (now has
a real dispatch regression test).
- pi/gsd.cjs: /gsd handler now (args, ctx) - tokenizes (quote-aware, via the
shipped hooks/lib/git-cmd.js) + dispatches real family/subcommand (not hardcoded
query/help); gsd_invoke gets a TypeBox (JSON-schema-fallback) parameters schema +
consumes params; getArgumentCompletions; before_provider_request active-model
steering (fail-open on null resolution); functional session_start /
before_agent_start / session_before_compact hook bridges (spawn the shipped GSD
hook scripts).
- EXTENSION_EVENT_SURFACES.pi expanded from ['tool_call'] to the full 30-event
vocabulary.
Docs (host-integration matrix + how-to) + changeset (Added).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The issue's "0 conditional branches" premise missed a live one: the `!isZcode`
shared-hooks exclusion (bin/install.js). Fold it onto descriptor-driven
hostBehaviors.skipSharedHooksInstall:true (zcode's golden has zero hook files —
byte-parity verified) and drop the now-unused isZcode destructure. Zero live
runtime==='zcode'/isZcode branches remain (AC2 source-grep guard over
bin/install.js + install-engine.cts + surface.cts + runtime-artifact-conversion.cts).
The 6 CLI-bookkeeping zcode mentions (--zcode flag, menu, roster, help) stay.
Reference test (declarative-reference-zcode.test.cjs): profileOf → declarative-cli;
createDeclarativeAdapter({runtime:'zcode'}).kind → declarative; a real install emits
the invocable nested-skills/commands/agents surface (no hooks); negotiateHostCapabilities
fail-closes (empty/corrupt descriptor; the nested/maxDepth undocumented sub-axes degrade
to most-restrictive); validateCapability clean.
UPGRADES documented as BLOCKED (verified doc gaps — NOT guessed, to avoid a
non-functional false-green): both of ZCode's documented capabilities lack a published
on-disk config format.
- Hook automation: zcode.z.ai/en/docs/plugin documents the Hook component only as
"automation hooks triggered on specific events" (capability detected from directory
layout) — no config file format/location/event schema. Cannot faithfully wire.
- MCP registration: zcode.z.ai/en/docs/mcp-services says servers are "stored in the
.zcode configuration file" (UI-only) with no documented on-disk filename/path/schema —
exactly the settings-filename gap the issue AC anticipated.
Both are documented (with the search trail) in the capability matrix + how-to, per AC4's
block-documentation clause; hookBus/transport stay declared for when ZCode publishes the
formats. No hook scripts or MCP artifacts added → no golden change, no other-runtime impact.
Golden: byte-identical for all 16 runtimes (the fold is byte-parity; no upgrade artifacts).
Matrix ## zcode EoS note + how-to; changeset (Changed). capability-registry regenerated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>