Commit Graph

4513 Commits

Author SHA1 Message Date
Tom Boucher
aaf74878f7 fix(#2138): push the track_shipping ship-note onto the PR branch [ci skip]
track_shipping committed the STATE ship-note ('Phase N shipped — PR #N') AFTER
create_pr and never pushed it, so the commit stayed local-only. When the GitHub
PR merged (especially fast/auto-merge) the ship-note was not in the source branch
and never reached the default branch — STATE's ship-status was silently lost,
recoverable only by STATE self-heal on the next /gsd-start.

Push the ship-note commit onto the PR branch with a [ci skip] trailer. GitHub
honors [ci skip]/[skip ci], so this lands the note on merge without triggering a
redundant pipeline, and preserves the PR number in STATE.

Recaptures the 18 golden-install-parity fixtures + the workflow-size baseline
(only the ship.md entry changed in each).

Closes #2138
2026-07-12 15:55:39 -04:00
Tom Boucher
69ac4d0aa7 test(#2138): guard track_shipping pushes the ship-note (source-text contract)
ship.md IS the product the runtime loads. Assert its track_shipping step pushes
the committed ship-note onto the PR branch and carries a [ci skip] trailer, so a
regression to the local-only commit (the #2138 bug) is caught.
2026-07-12 15:55:39 -04:00
Tom Boucher
b145ff6177 docs(#2136): backfill PR number in changeset fragment 2026-07-12 15:34:59 -04:00
Tom Boucher
58b20c31f8 fix(#2136): migrate ALL operator-facing date sites to localToday (anti-pattern elimination)
The UTC-slice anti-pattern (deriving a calendar day a human reads by slicing a
UTC instant) remained in several operator-facing sites beyond the original
seamed last_activity set. Eliminate it everywhere a human reads the value as a
calendar day — do not leave known bad code in place:

- commands.cts cmdTodoComplete + cmdScaffold: completion/scaffold dates.
- gsd2-import.cts: migrated STATE.md 'Last activity' / 'Last session'.
- template.cts: plan frontmatter 'completed:' date.
- verify.cts: health --repair session-log date + '(Backfilled: <date>)' header.
- workstream.cts: workstream-create 'Last Activity' / 'created' + archive dirname.
- init.cts: JSON-bundle 'date' (-> localToday) + 'timestamp' (-> nowIso) at all
  three sites; drop the now-dead 'const now = new Date()' in cmdInitTodos /
  cmdInitMapCodebase (cmdInitQuick keeps it for the local branch-id derivation).
- state.cts: prune-archive '## Pruned <date>' header.
- state-transition.cts: the 7 seamed last_activity writes (prior commit).

No realClock.today() / .clock.today() / raw new Date().toISOString().split('T')
operator-facing sites remain in src/. Rebuilds the tracked
bin/lib/state-transition.cjs artifact to match.
2026-07-12 15:34:59 -04:00
Tom Boucher
9ab328917b docs(#2136): add changeset fragment for local calendar day fix 2026-07-12 15:34:59 -04:00
Tom Boucher
71edb27fe1 fix(#2136): route operator-facing date fields through local clock day
clock.today() derived the calendar day by slicing a UTC ISO instant
(nowIso().split('T')[0]), so in any negative-UTC-offset zone during UTC's early
hours last_activity named a day the operator had not reached yet — ahead of
last_updated's local date, written by the same call.

- Add Clock.localToday(): host-local YYYY-MM-DD via getMonth/getDate/
  getFullYear, honoring the same GSD_NOW_MS pin as today()/nowIso().
- Route operator-facing date-only fields through localToday(): last_activity
  (state-transition ×7, state.cts), roadmap 'completed <date>' (phase.cts,
  roadmap.cts), milestone completion date (milestone.cts), todo/scaffold
  completion (commands.cts — now threaded through the realClock seam).
- Leave today() (UTC) as the source for internal/cosmetic stamps.

Closes #2136
2026-07-12 15:34:59 -04:00
Tom Boucher
51b6e3c35c test(#2136): add localToday regression + mirror fake/inline clock stubs
Adds a dedicated regression (tests/fix-2136-clock-local-today.test.cjs):
- realClock.localToday() returns the LOCAL calendar day under a pinned instant
  + TZ (America/Chicago → 2020-06-14, the issue's repro instant).
- realClock.today() is unchanged (UTC) — internal/cosmetic stamps stay UTC.
- localToday === today on a UTC host (no spurious divergence).
- makeFakeClock mirrors localToday (drop-in Clock substitute).
- field-level: a 'sync' transition with a split clock (today≠localToday)
  writes the LOCAL day into Last Activity, proving the wiring uses localToday.

Mirrors localToday() in the fake clock helper and the inline fixedClock stubs
in state-transition.test.cjs / state-rebuild.test.cjs (the Clock interface now
requires it).
2026-07-12 15:34:59 -04:00
Tom Boucher
09a7cc9fea Merge pull request #2215 from open-gsd/fix/2135-milestone-name-clobber
fix(#2135): anchor milestone heading regex + strip delimiter, widen preserve guard
2026-07-12 14:21:50 -04:00
Tom Boucher
58b2aa1985 Merge branch 'next' into fix/2135-milestone-name-clobber 2026-07-12 12:59:02 -04:00
Tom Boucher
a02fe1213c Merge pull request #2214 from open-gsd/fix/2133-fast-md-log-to-state-schema-gate
fix(#2133): correct fast.md log_to_state column-count gate (NF-2)
2026-07-12 12:58:46 -04:00
Tom Boucher
a5110c4b8c Merge branch 'next' into fix/2133-fast-md-log-to-state-schema-gate 2026-07-12 12:42:15 -04:00
Tom Boucher
d474b5010d docs(#2135): backfill PR number in changeset fragment 2026-07-12 11:59:51 -04:00
Tom Boucher
af8d650da9 docs(#2135): add changeset fragment for milestone_name clobber fix 2026-07-12 11:41:14 -04:00
Tom Boucher
3bc53c8116 fix(#2135): anchor milestone heading regex + strip delimiter, widen preserve guard
getMilestoneInfo's `##` heading regex was unanchored (no `^`/`m`), so it
matched a `##` quoted mid-line inside a Milestones bullet and captured a
delimiter-led fragment into milestone_name — clobbering the curated name on
every phase transition.

roadmap-parser.cts (load-bearing):
- Consult the 🚧 name-bearing marker FIRST (reorder; it already existed but was
  shadowed by a spuriously-successful heading match).
- Anchor the `##` regex to line start (`^` + `m` flag) so a heading quoted
  in backticks/prose can no longer match.
- stripLeadingDelimiter removes a leading em/en-dash/colon/hyphen run that
  .trim() cannot (the `## vX.Y — Name` convention).

state.cts (defense in depth):
- Widen the #948 preserve guard from 'derived equals placeholder' to
  'derived does not look like a name' (non-empty, not placeholder, not
  punctuation-led), so a future bad derive preserves the curated name instead
  of silently overwriting it.

Closes #2135
2026-07-12 11:39:59 -04:00
Tom Boucher
5cbe250f36 test(#2135): add 5-case regression for getMilestoneInfo name derive
Adds the issue's verification matrix (cases A-E): a 🚧 bullet quoting a
nameless ## heading in backticks (the corruption), a nameless heading plus a
🚧 sub-heading carrying the name, canonical colon/em-dash shapes (no
regression), a 🚧 bullet only, and an anchored-regex guard proving a ##
heading quoted mid-line in backticks never matches.
2026-07-12 11:39:59 -04:00
Tom Boucher
f5370ef9c0 docs(#2133): backfill PR number in changeset fragment 2026-07-12 11:26:24 -04:00
Tom Boucher
cb48b2b48f test(#2133): drop no-op try/finally in runAgainst helper (review) 2026-07-12 11:13:48 -04:00
Tom Boucher
30469ba751 test(#2133): drop unused os import (lint clean) 2026-07-12 11:08:20 -04:00
Tom Boucher
c6b4304bab test(#2133): regenerate golden + workflow-size baselines for fast.md
The fast.md log_to_state fix changes an installed workflow file, so the per-
runtime golden-install-parity fixtures (18 runtimes) and the workflow-size
baseline are recaptured. Diff is exactly one entry per fixture (the fast.md
hash) and one baseline byte count — no spurious drift.
2026-07-12 10:48:43 -04:00
Tom Boucher
dca8bd9daa test(#2133): scope NF-2 assertion to the executable awk formula
The first draft banned the literal /NF-1/ anywhere in the block, but the
explanatory comment legitimately references 'NF-1' to document the off-by-one
root cause. Match the COL_COUNT awk assignment specifically so the structural
guard targets the executable formula, not the prose.
2026-07-12 10:48:43 -04:00
Tom Boucher
6504afa843 Merge pull request #2213 from open-gsd/fix/2116-surface-bare-require
fix(#2116): use resolvable paths in surface.md require + correct package name
2026-07-12 10:41:41 -04:00
Tom Boucher
1153eefedd Merge branch 'next' into fix/2116-surface-bare-require 2026-07-12 10:33:02 -04:00
Tom Boucher
c59a4a7abb docs(#2133): add changeset fragment for fast.md log_to_state fix 2026-07-12 10:28:40 -04:00
Tom Boucher
78b9b04f1d fix(#2133): correct fast.md log_to_state column-count gate (NF-2)
The guard used `awk -F'|' '{print NF-1}'` but a markdown header has a leading
and trailing pipe, so NF counts (real columns + 2); NF-1 is always one too
high. The `-eq 5` test was therefore unsatisfiable for the very 5-column
header quick.md writes, so /gsd-fast has never appended a Quick Task row since
PR #85 (regression closing #27).

- Count real columns with NF-2 (5 for the 5-col header, 6 for the 6-col).
- Accept 5 OR 6 columns (quick.md Step 7b writes both shapes).
- Select the appended row template by the detected count so its cell count
  always matches the header — keeps #27 fixed for the validate-mode table.

Closes #2133
2026-07-12 10:27:55 -04:00
Tom Boucher
e30f9370d3 test(#2133): execute fast.md log_to_state guard against both schemas
Replaces the removed prose-regex test (bug-3805-*) that let the off-by-one
ship green. Extracts the actual bash block deployed in fast.md's log_to_state
step and EXECUTES it against real STATE.md fixtures, asserting on the
filesystem result: a row is appended with a cell count matching the header for
both the 5-column (non-validate) and 6-column (validate) schemas, and an
unrecognized schema still skips with a warning.
2026-07-12 10:27:55 -04:00
Tom Boucher
87d1d5ac67 chore(#2116): correct changeset type and regenerate gsd-surface SKILL.md
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>
2026-07-12 09:54:23 -04:00
Tom Boucher
d221d0f830 docs(#2116): backfill PR number in changeset 2026-07-12 01:42:01 -04:00
Tom Boucher
872bb5b94d Merge pull request #2212 from open-gsd/chore/sync-next-version-1.7.0-rc.6
chore: sync next package version to 1.7.0-rc.6
2026-07-12 01:40:52 -04:00
github-actions[bot]
27f69cc489 chore: sync next package version to 1.7.0-rc.6 2026-07-12 05:40:45 +00:00
Tom Boucher
64c4a105f6 fix(#2116): use resolvable paths in surface.md require + correct package name
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'.
2026-07-12 01:32:12 -04:00
Tom Boucher
e6aef86cc7 Merge pull request #2211 from open-gsd/fix/2198-security-dead-scan-exports
fix(#2198): remove dead scan exports, correct injection-scan docs
2026-07-12 01:06:02 -04:00
Tom Boucher
1efa05b861 Merge branch 'next' into fix/2198-security-dead-scan-exports 2026-07-12 00:51:23 -04:00
Tom Boucher
dff6245de9 docs(#2198): backfill PR number in changeset 2026-07-12 00:37:55 -04:00
Tom Boucher
8022c5c864 fix(#2198): remove dead scan exports, correct injection-scan docs
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
2026-07-12 00:27:31 -04:00
Tom Boucher
89f991e9fc Merge pull request #2209 from open-gsd/fix/2117-audit-milestone-not-validated
fix(#2117): distinguish not-yet-validated phase from validated failure in audit-milestone
2026-07-12 00:25:55 -04:00
Tom Boucher
14b755d928 Merge branch 'next' into fix/2117-audit-milestone-not-validated 2026-07-12 00:03:52 -04:00
Tom Boucher
7252807194 Merge pull request #2210 from open-gsd/feat/2103-eos-vscode
feat(#2103): drive VS Code through the Embeddable Orchestration System (ADR-1239)
2026-07-11 23:39:21 -04:00
Tom Boucher
a0fafedfa0 feat(#2103): drive VS Code through the Embeddable Orchestration System (ADR-1239)
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>
2026-07-11 23:24:42 -04:00
Tom Boucher
b55a2e7655 fix(#2117): distinguish not-yet-validated phase from validated failure in audit-milestone
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>
2026-07-11 22:47:51 -04:00
Tom Boucher
96b632d4eb Merge pull request #2208 from open-gsd/docs/2207-status-lifecycle-ownership
docs(#2207): ADR-2207 — STATE.md `Status` lifecycle & ownership
2026-07-11 22:42:28 -04:00
Tom Boucher
fdf01ba588 docs(#2207): ADR-2207 — STATE.md Status lifecycle & ownership 2026-07-11 22:34:04 -04:00
Tom Boucher
97297d7e3f Merge pull request #2205 from open-gsd/feat/2102-eos-pi
feat(#2102): make pi a first-class installable runtime + fix its dispatch (ADR-1239)
2026-07-11 21:23:50 -04:00
Tom Boucher
79d7657eff feat(#2102): make pi a first-class installable runtime + fix its dispatch (ADR-1239)
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>
2026-07-11 21:07:38 -04:00
Tom Boucher
246f566ee1 Merge pull request #2195 from open-gsd/feat/2101-eos-zcode
feat(#2101): dogfood ZCode through the EoS declarative adapter + fold shared-hooks exclusion (ADR-1239)
2026-07-11 17:29:03 -04:00
Tom Boucher
a2c7b879d0 feat(#2101): dogfood ZCode through the EoS declarative adapter + fold shared-hooks exclusion (ADR-1239)
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>
2026-07-11 17:14:48 -04:00
Tom Boucher
e7063e8630 Merge pull request #2193 from open-gsd/feat/2182-eos-registry
feat(#2182): add EoS Registry discoverability catalog
2026-07-11 16:57:27 -04:00
Tom Boucher
676ec30374 chore(#2182): update EoS registry changeset PR number (#2193)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 16:41:56 -04:00
Tom Boucher
0137f9b76f Merge pull request #2188 from open-gsd/feat/2182-capability-registry
feat(#2182): add Community Capability Registry discoverability catalog
2026-07-11 16:38:31 -04:00
Tom Boucher
fe33017a65 Merge pull request #2190 from open-gsd/feat/2100-eos-windsurf
feat(#2100): drive Windsurf through the EoS descriptor + wire Cascade's blocking hook bus (ADR-1239)
2026-07-11 16:19:05 -04:00
Tom Boucher
bd613566cb feat(#2100): drive Windsurf through the EoS descriptor + wire Cascade's blocking hook bus (ADR-1239)
Fold all 10 residual isWindsurf branches in bin/install.js onto descriptor-driven
hostBehaviors (byte-parity — no fold changes any install output):
- 2 dead destructures dropped (uninstall, finishInstall); the dead
  `else if (isWindsurf)` legacy agent-loop arm removed (windsurf ∈
  _DESCRIPTOR_AGENTS_RUNTIMES → unreachable).
- skipSharedHooksInstall:true folds the two `!isWindsurf` shared-hooks exclusions.
- legacyDevinSkillsCleanup:true folds the `.devin`→`.windsurf` one-time cleanup gate.
- installsCommandBodiesForWorkflowDelegation:true folds the #1629 command-body copy
  (workflow-delegation target — load-bearing; local-install verified intact).
- verificationStyle:"windsurf-workflows" folds the workflow-count report.
- Corrected stale _LEGACY_SCAN_SUBDIR_NAMES + hooks-json manifest comments (cursor + windsurf).
Zero live runtime==='windsurf'/isWindsurf branches remain across bin/install.js,
install-engine.cts, surface.cts, runtime-artifact-conversion.cts (AC2 guard scans all four).

UPGRADE (Cascade hook bus): wire GSD's write/command safety guards into Windsurf's
native hook bus. New hooksSurface 'windsurf-hooks-json' (VALID_HOOKS_SURFACES 7→8, GATE A
profile-marker-only allowlist, the HooksSurface union) + writeWindsurfHooksJson
(Cursor-templated, Cascade's flat {hooks:{<event>:[{command}]}} shape) writing
.windsurf/hooks.json with two BLOCKING pre-hooks:
- pre_write_code → gsd-windsurf-pre-write.js: blocks writes to a file outside the
  active git worktree / into .git internals.
- pre_run_command → gsd-windsurf-pre-command.js: conservative destructive-command
  deny-list (rm -rf of root/home incl. sudo/env/path-prefixed forms; fork bombs;
  force-push refspec forms — HEAD:main, +main, --force/-f — to main/master/next).
Both use Cascade's protocol (stdin JSON, exit 2 + stderr to block, exit 0 to allow,
fail-open on error/timeout). Tokenize-based classifier (no catastrophic-backtracking regex;
4096-char cap) with the fail-closed false-positives fixed post-review.

The 4 advisory GSD guards + pre_mcp_tool_use + 5 post_* logging events are deliberately
NOT wired: Cascade has no context-injection channel for advisory hooks and GSD has no MCP
guard — porting them would be non-functional padding (documented; codebuddy #2098 / copilot
#2099 faithful-subset precedent). extendedHookEvents stays [].

Golden: the 2 guard scripts ship in the shared hook bundle (HOOKS_TO_COPY + the shared
managed-hooks-registry), exactly like cursor's 6 gsd-cursor-*.js scripts — so the 8
shared-bundle runtimes' fixtures gain the 2 inert windsurf scripts + the registry hash
(functionally inert for non-windsurf; the established cursor pattern). No install-output
change beyond that (the folds are byte-parity; skip-bundle runtimes untouched). New scripts
registered in managed-hooks-registry + build-hooks + INVENTORY. Tests: declarative-reference-
windsurf (adapter/axes/fail-closed + AC2 guard) + windsurf-hooks-bridge (live exit-2 blocking
+ allow/fail-open + ReDoS-bound + writer/reconcile/remove idempotency); VALID_HOOKS_SURFACES
pin updated to 8. Matrix hookBus delta + changeset (Changed). capability-registry regenerated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 16:04:24 -04:00