Commit Graph

4450 Commits

Author SHA1 Message Date
Tom Boucher
17aa35fba7 feat(#2097): migrate Augment onto EoS declarative adapter + settings.json MCP companion (ADR-1239)
Fold augment's runtime-literal conversion branches onto descriptor-driven
hostBehaviors and delete dead code:
- Site A (_applyRuntimeRewrites case 'augment'): the 4 ~/.augment dot-dir
  regexes now derive from getDirName('augment') via escapeRegExp (byte-
  identical; getDirName('augment')==='.augment') — no runtime literal.
- Site B (applyRuntimeContentRewritesForCommandsInPlace): the
  `if (runtime==='augment')` markdown-converter branch now reads
  runtime.hostBehaviors.commandBodyConverter and dispatches through a local
  COMMAND_BODY_CONVERTERS map (degrade-closed on unknown/absent name).
- Deleted dead `claudeToAugmentTools` map (zero refs; orphaned by ADR-1508
  single-sourcing) and the unreachable `else if (isAugment)` agent-conversion
  branch (augment ∈ _DESCRIPTOR_AGENTS_RUNTIMES → gated out upstream).
- Incidental orphan cleanup (no-defer): removed the equally-unreachable
  `else if (isTrae)` agent-conversion arm left behind by trae's already-merged
  migration #2094 (trae ∈ _DESCRIPTOR_AGENTS_RUNTIMES, same upstream gate).
  copilot/windsurf/codebuddy arms are removed by their own pending migrations.

UPGRADE 3 (transport:mcp): register the GSD companion MCP server in Augment's
settings.json under mcpServers.gsd (Augment hosts MCP in settings.json, not a
standalone file). mergeGsdMcpServerIntoSettings mutates the in-memory settings
object finishInstall already writes (gated on hostBehaviors.mcpCompanion===
'settings-json'); non-destructive + idempotent; symmetric uninstall removal.
settings.json is golden-excluded, so no golden change. UPGRADE 1 (named/
background dispatch) + UPGRADE 2 (settings-json hook bus, Claude dialect)
were already live in production — this adds tests exercising both.

Golden: byte-identical for all 16 runtimes (folds preserve regex behavior;
MCP lives in golden-excluded settings.json) — verified by a real double-install
tree diff. Tests: declarative-reference-augment (adapter/axes/fail-closed/
undocumented-sub-axes + source-grep guard scoped to conversion-logic branches)
+ augment-upgrades (dispatch negotiation, hook-bus live install, MCP add/
idempotent/preserve/uninstall). Matrix + connect-gsd-mcp-server + a stale
install-on-your-runtime hook-ownership claim corrected; changeset (Changed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 04:47:56 -04:00
Tom Boucher
41d544248b Merge pull request #2165 from open-gsd/feat/2096-eos-antigravity
feat(#2096): migrate Antigravity onto EoS declarative adapter + permission-writer + MCP companion (ADR-1239)
2026-07-11 03:21:49 -04:00
Tom Boucher
5695522d5f feat(#2096): migrate Antigravity onto EoS declarative adapter + permission-writer + MCP companion (ADR-1239)
Fold all antigravity literal branches into descriptor-driven reads:
getConfigDirFromHome (→ configHome.kind 'dot-home-nested'), projectLocalHookPrefix
(→ hostBehaviors.hookPathStyle 'raw'), applyAgentPathRewrites (→ noPathRewrite),
getProjectInstructionFile (→ projectInstructionFile 'GEMINI.md'); removed the dead
inline convertClaudeAgentToAntigravityAgent branch + dead isAntigravity
destructures (antigravity is already on the descriptor-agents path). subagentToolkit
flipped undocumented→full (Context7: antigravity.google/docs/cli/features);
namedDispatch/nested/maxDepth/backgroundDispatch stay undocumented. Byte-identical
golden parity for all 16 runtimes.

UPGRADE 1 (permission-writer): permissionWriter 'antigravity' + configureAntigravityPermissions
merges a scoped permissions.allow block (GSD's own tree + hooks) into Antigravity's
settings.json — non-destructive, idempotent, symmetric uninstall. Added to
VALID_PERMISSION_WRITERS + the FinishPermissionWriter union.
UPGRADE 2 (MCP companion): configureAntigravityMcpConfig writes mcp_config.json
registering the gsd-core companion MCP server (Gemini-successor mcpServers schema,
best-effort — raw schema unpublished). Both writers dispatch from finishInstall.
settings.json is golden-excluded (HOOK_CONFIG_FILES); mcp_config.json (portable,
no absolute paths) is golden-tracked → only antigravity.json changes.

Tests: declarative-reference-antigravity extended (source-grep guard across 4
modules, fail-closed for the 4 undocumented sub-axes, validator acceptance) +
antigravity-upgrades (permission-writer + mcp_config live-install, idempotency,
user-preservation). Matrix + ADR-1016 + capability-manifest + CONTEXT.md +
connect-gsd-mcp-server docs updated; changeset (Changed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 03:07:01 -04:00
Tom Boucher
e3a8c063b8 Merge pull request #2159 from open-gsd/feat/2095-eos-kimi
feat(#2095): migrate Kimi CLI onto EoS imperative adapter + native hook-bus + background dispatch (ADR-1239)
2026-07-11 01:06:34 -04:00
Tom Boucher
ab04916682 feat(#2095): migrate Kimi CLI onto EoS imperative adapter + native hook-bus + background dispatch (ADR-1239)
Fold all runtime==='kimi'/isKimi logic branches into descriptor-driven
hostBehaviors (localInstallDeferred, verificationStyle, agentManifestStyle,
reapplyCommand, doneBannerStyle) + add 'kimi' to _DESCRIPTOR_AGENTS_RUNTIMES.
Kimi's skills/kimi-agents dispatch was already descriptor-driven (converter-by-
name + kimi-agents kind). Zero isKimi/runtime==='kimi' branches remain.

UPGRADE 1 (native hook bus): new hooksSurface 'kimi-hooks-toml' + a marker-
delimited config.toml [[hooks]] emitter (buildKimiHooksTomlBlock/writeKimiHooksToml
in runtime-hooks-surface.cts; resolveKimiHooksTomlDir in runtime-homes.cts).
GSD's lifecycle hooks now wire into Kimi's native ~/.kimi/config.toml (Context7-
confirmed path) at SessionStart/PreToolUse/Stop/PreCompact/SubagentStart/
SubagentStop — kimi becomes a hooks/ consumer (the 3 && !isKimi exclusion guards
removed). config.toml holds absolute install paths so it's golden-excluded via
an exact relative-path (.kimi/config.toml), not a basename (which would blind
Codex's config.toml). New hooksSurface value added to the closed enum in
capability-validator + runtime-config-adapter-registry.
UPGRADE 2 (background dispatch): flip dispatch.backgroundDispatch true (Kimi's
Agent tool takes run_in_background; root agent already gets the Agent tool), so
negotiation no longer flattens dispatch. subagentToolkit stays 'undocumented'
per AC (coder/explore/plan have distinct tool policies).
MCP transport explicitly deferred (no installer-driven MCP for any runtime).

Golden: only kimi.json changes (hooks/ scripts now installed); all 15 others +
claude-local byte-identical (kilo/zcode keep their own exclusions). Tests:
kimi-imperative-reference (adapter/axes/fail-closed/hostBehaviors + source-grep
guard) + kimi-upgrades (config.toml [[hooks]] SessionStart + marker idempotency
+ backgroundDispatch negotiation). CONTEXT.md glossary + matrix + how-to updated;
changeset (Added).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 00:53:34 -04:00
Tom Boucher
a022eec117 Merge pull request #2157 from open-gsd/feat/2094-eos-trae
feat(#2094): migrate Trae onto EoS imperative adapter + SOLO stage-metadata upgrade (ADR-1239)
2026-07-10 21:46:55 -04:00
Tom Boucher
f744635b3d feat(#2094): migrate Trae onto EoS imperative adapter + SOLO stage-metadata upgrade (ADR-1239)
Fold trae logic branches into descriptor-driven reads: skipSharedHooksInstall
gates (dropped && !isTrae), and the case 'trae' path-rewrite arm now computes
the self-alias from the descriptor-driven dirName (.trae). Dead isTrae bindings
removed from uninstall/writeManifest/finishInstall. trae's skills dispatch was
already descriptor-driven (converter-by-name). RUNTIME_CONTENT_DISPATCH.trae is
left as a runtime-keyed table registration (its regex/callback rewrites can't be
a byte-identical descriptor map — matches cursor/windsurf/cline). trae stays in
RUNTIME_FLAG_IDS: isTrae still gates the agents-converter selection (agents out
of scope; removal gated on the cross-runtime agents-dispatch migration).
Byte-identical golden parity for all 16 runtimes.

UPGRADE: SOLO stage/trigger metadata — emitted Trae SKILL.md now carries
stage: workflow (descriptor-gated via hostBehaviors.soloStageMetadata) so
Trae's SOLO Agent can auto-invoke GSD skills at the corresponding stage. Field
shape is best-effort/inferred (Trae publishes no formal schema). trae.json
golden regenerated.

Tests: trae-imperative-reference (adapter/axes/fail-closed shouldFlattenDispatch
+ no runtime==='trae' source-grep, isTrae exempted for agents) + trae-upgrades
(stage: workflow on installed SKILL.md, descriptor-gated). Matrix note +
changeset added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:31:10 -04:00
Tom Boucher
d010ea13d4 Merge pull request #2156 from open-gsd/feat/2093-eos-kilo
feat(#2093): migrate Kilo onto EoS imperative adapter + hook-bus/model/MCP/dispatch upgrades (ADR-1239)
2026-07-10 20:27:27 -04:00
Tom Boucher
f014ec83bd feat(#2093): migrate Kilo onto EoS imperative adapter + hook-bus/model/MCP/dispatch upgrades (ADR-1239)
Fold remaining isKilo logic branches into descriptor-driven reads:
finishPermissionWriter (uninstall cleanup), skipSharedHooksInstall (hooks
copy), and a skills converter-name registry (the artifactLayout.converter
field is now load-bearing, not decorative). frontmatterDialect stays the
documented dispatch key for frontmatter (no descriptor field for it). Dead
isKilo destructure bindings removed. Byte-identical golden parity for all 16
runtimes (opencode, which shares kilo's combined-family path, verified clean).

UPGRADE 1 (hook bus): install .kilo/plugins/gsd-core.js native plugin +
extensionEvents:"kilo" + EXTENSION_EVENT_SURFACES.kilo (OpenCode-fork bus).
UPGRADE 2 (active model): populate runtimeTierDefaults.kilo + thread
modelOverride through convertClaudeToKiloFrontmatter — model no longer stripped
from agents. UPGRADE 3 (MCP): document the gsd-core MCP companion under kilo's
mcp config key. UPGRADE 4 (named dispatch): agents/*.md mode:subagent roster is
the Task-tool dispatch surface (tested); subagentToolkit stays 'undocumented'
per AC so dispatch degrades to 'degraded' by design.

Model-catalog single-source edit ripples the shared model-catalog.json hash
into all 16 golden fixtures (expected). Inline defect fixes (no-defer): stale-
bake-guard resolveAgentDir 'agent'->'agents' (was a silent no-op for opencode/
codex), hardcoded 'Removed OpenCode plugin' uninstall log -> generic, and the
connect-gsd-mcp-server.md OpenCode mcpServers->mcp doc error.

Tests: kilo-imperative-reference (adapter/axes/fail-closed/degradation/
hostBehaviors + widened isKilo source-grep across 4 modules) + kilo-upgrades
(plugin parity+load, model-override converter, agents dispatch surface, MCP
doc). Matrix + how-to + config docs updated; changeset added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 19:55:15 -04:00
Tom Boucher
61d04daacf Merge pull request #2153 from open-gsd/feat/2092-eos-qwen
feat(#2092): migrate Qwen Code onto EoS imperative adapter + native subagents + SubagentStart (ADR-1239)
2026-07-10 16:14:39 -04:00
Tom Boucher
c6ce110efa feat(#2092): migrate Qwen Code onto EoS imperative adapter + native subagents + SubagentStart (ADR-1239)
Fold all runtime==='qwen'/isQwen logic branches (skill-priority frontmatter,
branding/path rewrites, legacy commands/gsd cleanup, hyphen-namespace
normalization, RUNTIME_CONTENT_DISPATCH, hooks-surface label) into
descriptor-driven runtime.hostBehaviors on capabilities/qwen/capability.json,
read via _hostBehaviors(). Shared claude/qwen/hermes legacy-migration branches
in install-engine.cts folded to descriptor flags (claude+hermes descriptors
updated; FALLBACK_HOST_BEHAVIORS.claude floored). Byte-identical golden parity
for qwen/hermes/claude(global+local).

UPGRADE 1: native .qwen/agents/*.md subagent projection — new agents
artifact-layout kind + convertClaudeAgentToQwenAgent converter (name +
description + tools YAML block list; color/model dropped). qwen routed onto
the descriptor-driven agents path (_DESCRIPTOR_AGENTS_RUNTIMES).
UPGRADE 2: SubagentStart hook wired into extendedHookEvents + the
descriptor-gated hook-writer loop (activates only for qwen).

Tests: qwen-imperative-reference (adapter/axes/fail-closed/hostBehaviors +
no runtime==='qwen' source-grep across 4 files) + qwen-upgrades (agents file
validity + SubagentStart mirrors SubagentStop, descriptor-gated). Docs matrix
+ how-to updated; changeset added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 15:44:19 -04:00
Tom Boucher
91a68ae8e6 Merge pull request #2149 from open-gsd/fix/2104-foreign-prefix-sibling-commands
fix(#2104): guard foreign-prefix collapse in init execute-phase/verify-work/phase-op
2026-07-10 15:04:38 -04:00
Tom Boucher
52c938d7e5 Merge pull request #1979 from davesienkowski/feat/1867-ui-consideration-probe
feat(#1867): ui-consideration probe — 3rd probe-core adapter for UI state coverage
2026-07-10 15:04:11 -04:00
Dave
a8ff8fbb30 docs(#1867): specify propose-then-confirm --auto behavior in ui-phase Step 9.5 (review Minor) 2026-07-10 14:41:12 -04:00
Dave
31a500b970 docs(#1867): replace stale plan-phase.md:921 line-pointer with section-name reference (review #6) 2026-07-10 14:40:03 -04:00
Dave
20608cba90 chore(#1867): regen goldens + size baselines after merge onto next
next advanced to a3b9cbae (incl. #1820 specless-rail merge); regenerate
golden-install-parity fixtures + workflow-size-baseline authoritatively.
Manifest + agent baseline already in sync. plan-phase.md conflict
hand-merged to keep both EDGE_ABSENT/PROHIB_ABSENT and UI Considerations.
2026-07-10 14:40:03 -04:00
Dave
830670f288 docs(#1867): clarify Step 9.5 manual element-extraction is by-design (review Minor 1)
trek-e's re-review asked to either mechanize the Step 9.5 element
extraction or add an inline note justifying why it stays manual, so a
future maintainer doesn't read it as an oversight.

Verified the premise: the requirement-side edge-probe path (spec-phase
Step 5.5) it mirrors is ALSO a hand-populated heredoc + fail-loud
<replace:> placeholder guard — not a mechanical parse. The UI probe
mirrors that idiom verbatim. Mechanizing would be worse: a UI-SPEC has no
single machine-parseable "elements" column (surfaces are spread across the
design-token tables, Copywriting, and researcher-named prose), so a
regex/table parse would fail-OPEN (miss a prose-named surface, or feed a
design-token row as a bogus element).

Expanded the inline comment to state the parity + the fail-open rationale
explicitly. No logic change; regenerated the workflow size baseline and
the 16 golden-install-parity fixtures for the +847 B comment.

Refs #1867

Claude-Session: https://claude.ai/code/session_017vYn26e3nkDNxcpty1ciPJ
2026-07-10 14:40:03 -04:00
Dave
dc0c434141 chore(#1867): point changeset at PR #1979
Claude-Session: https://claude.ai/code/session_01BKt4hgNZwXSeJYJtYAQUSS
2026-07-10 14:40:03 -04:00
Dave
a4d8eb4a78 chore(#1867): eslint-ignore generated ui-consideration-probe.cjs (SHIP-01)
ci-preflight (full test:unit) caught a Phase-1 registration gap: the
tsc-generated gsd-core/bin/lib/ui-consideration-probe.cjs was not in the
eslint.config.mjs global ignore list, failing 551-eslint-bin-lib-coverage
("each bin/lib/*.cjs is linted xor ignored according to migration state",
ADR-457/#537). Generated artifacts are ignored, not linted — added it next to
the sibling probe adapters (edge-probe/probe-core/prohibition-enforcement).

Claude-Session: https://claude.ai/code/session_01BKt4hgNZwXSeJYJtYAQUSS
2026-07-10 14:40:03 -04:00
Dave
c1756d0cd5 chore(#1867): register ui-consideration probe + regen install cascade (SHIP-01)
Ship-safe registration + regenerated snapshots for the #1867 UI-consideration
probe (Phase 3, SHIP-01):

- CONTEXT.md: PROBE.ui.{verification,axis,seam} predicates + ui-consideration
  -probe added to PROBE.family (machine-canon for the 3rd adapter, MIXED axis).
- agents/gsd-ui-{researcher,checker}.md: one @-include of
  references/ui-consideration-probe.md each (both under the 24576 agent cap).
- docs/INVENTORY.md + INVENTORY-MANIFEST.json: register the reference doc and
  the compiled ui-consideration-probe.cjs (inventory-manifest-sync green).
- tests/fixtures/golden-install-parity/*.json (16 runtimes): recaptured against
  a clean full build — folds in the deferred Phase-1 (ref doc, plan-phase lift)
  and Phase-2 (ui-phase step, UI-SPEC section) install-surface changes.
- tests/agent-size-baseline.json: ratcheted the two grown UI agents.
- .changeset/vivid-orcas-chatter.md: type Added (pr updated at PR-open).

Inventory/golden/size gates green; lint:ci + lint:docs + lint:changeset green.
The plan-phase.md PRE_PHASE6 ceiling stays RED pending #1852 (unchanged).

Claude-Session: https://claude.ai/code/session_01BKt4hgNZwXSeJYJtYAQUSS
2026-07-10 14:40:03 -04:00
Dave
4bce86650d feat(#1867): add UI Considerations section to UI-SPEC template (WIRE-02)
Record resolved UI-state considerations in the UI-SPEC, backward-compatibly
(Phase 2, WIRE-02). templates/UI-SPEC.md gains a '## UI Considerations' section
(analog of SPEC '## Edge Coverage') after '## Copywriting Contract' — a
| Category | Element(s) | Status | Resolution / Reason | table with
covered/backstop/unresolved rows in the locked probe-core projectTruths format
the shipped plan-phase lift (plan-phase.md:921) reads. Empty/error COPY stays in
Copywriting; this section covers shape-rooted STATE and references those rows
(de-dup).

Tests: docs-fixtures parsed-heading assertion (UI Considerations present +
distinct from Copywriting Contract; allow-test-rule, parsed structure); typed
backward-compat (projectTruths(undefined/[])===[], old UI-SPEC still plans —
Hyrum), format-match, and idempotency (proposeElements determinism). The
template-structure test was the RED driver.

Install-parity cascade (INVENTORY-MANIFEST + 16 golden fixtures +
agent-size-baseline) deferred to Phase 3 SHIP-01, as planned.

Claude-Session: https://claude.ai/code/session_01BKt4hgNZwXSeJYJtYAQUSS
2026-07-10 14:40:03 -04:00
Dave
bea7196c4c feat(#1867): wire ui-consideration probe into ui-phase (WIRE-01)
Add the live ui-phase producer path for the UI-consideration probe (Phase 2,
WIRE-01). Two small exports on the Phase-1 adapter — proposeElements (the
propose-then-confirm view of detected kinds + applicable categories) and
autoResolve (the deterministic --auto floor that never dismisses and never
auto-backstops an unclassified item, #1110) — plus a post-verification
'## 9.5 UI-Consideration Probe' step in ui-phase.md mirroring spec-phase 5.5's
RUNTIME_DIR shim + fatal-invoke/malformed-report/zero-applicable fail-closed
guards, propose-then-confirm (the partial-cue recall mitigation), and the
'## UI Considerations' write-back in the shipped plan-phase.md:921 lift format.

autoResolve is the CODE floor; the covered-upgrade stays workflow prose (the
two-layer --auto). Un-upgraded backstops route to insufficient_spec ->
human_needed at verify, never a silent pass (#1154).

Tests: +8 typed (proposeElements shape/determinism, autoResolve never-dismiss,
partial-cue strict-subset) — structured-value only. ui-phase.md size baseline
ratcheted 15477->24447 (under DEFAULT cap). The plan-phase.md PRE_PHASE6 ceiling
stays RED pending #1852 (unchanged from Phase 1).

Claude-Session: https://claude.ai/code/session_01BKt4hgNZwXSeJYJtYAQUSS
2026-07-10 14:40:03 -04:00
Dave
6d7450c465 feat(#1867): lift UI Considerations into plan-phase must_haves (LIFT-01)
Separate *-UI-SPEC.md glob (edge-coverage glob exclusion at :746 left intact
- Hyrum/D-08); a terse lift bullet reusing the ## Edge Coverage rule verbatim
(covered -> truths string, backstop -> flat scalar {statement, verification:
backstop}, unresolved -> assumption; no new verb - ADR-550 #1278/#1154); and a
no-silent-drop checklist line. Lift logic lives ONLY in the plan-phase
workflow, never gsd-planner.md (D-10, agent-size cap).

NOTE: this grows plan-phase.md +862B, over the #1168 PRE_PHASE6 ceiling (94519)
by ~802B, so the workflow-size gates are RED until #1852's lazy-split lands the
-21KB headroom. Deliberately does NOT raise the ceiling constant (would collide
with #1820/#1835's in-flight raise). #1867 sequences after #1852; rebase +
regenerate the size baseline then.
2026-07-10 14:40:03 -04:00
Dave
29120ea0aa docs(#1867): ui-consideration-probe reference + docs-parity test (ADPT-02)
Reference doc mirrors edge-probe.md structure but links rather than re-argues;
states the MIXED-axis boundary (closed compiled shape-rooted subset here; open
UX subset - real-time/offline, a11y depth, i18n/RTL - prose-owned in
domain-probes.md). Docs-parity test pins doc taxonomy ids == code UI_TAXONOMY
ids and asserts disjointness from domain-probes.md topics (ADR-456
runtime-contract exemption, see #1867).
2026-07-10 14:40:03 -04:00
Dave
cf92eb2cf3 feat(#1867): ui-consideration-probe adapter over probe-core (ADPT-01/02/03, FILT-01)
Third probe-core adapter on the UI element/state axis, mirroring edge-probe.
Closed 8-id shape-rooted UI_TAXONOMY + element-cue relevance filter
(UI_CUES -> classifyElement -> applicableCategories); unclassified fail-loud
soft-signal (#1110); fail-closed on invalid authored element kinds. All
lifecycle/merge/validation delegated to probe-core verbatim (no fork). Item
question carried in the shared Item.probe field. LIFT-01 proven at the
probe-core primitive level (projectTruths/dispositionForUnverifiableTruth):
backstop considerations route to insufficient_spec, never a silent pass.

Tests assert typed returns off the built .cjs (no source-grep).
2026-07-10 14:40:03 -04:00
Tom Boucher
d45ea9e39d docs(changeset): backfill PR number (#2149) 2026-07-10 14:25:00 -04:00
Tom Boucher
f910352ea6 fix(2104): guard foreign-prefix collapse in init execute-phase/verify-work/phase-op
#2056 fixed the foreign-prefix collapse for init plan-phase only.
The identical defect remained in three sibling commands that called
findPhaseInternal/getRoadmapPhaseInternal without the guard:
- cmdInitExecutePhase
- cmdInitVerifyWork
- cmdInitPhaseOp

Extracted the #2056 guard into shared helpers (guardedFindPhase /
guardedGetRoadmapPhase) and routed all four init commands through them.
Deleted the local parsePhasePrefix/isForeignPrefixedPhaseQuery copies in
init.cts — the canonical export from phase-id.cts is now used directly,
eliminating the drift risk flagged by both reviewers.

Added 5 regression tests (3 reject + 2 accept-branch) mirroring the
#2056 plan-phase tests.
2026-07-10 14:24:14 -04:00
Tom Boucher
741d86f819 Merge pull request #2147 from open-gsd/chore/sync-next-version-1.7.0-rc.5
chore: sync next package version to 1.7.0-rc.5
2026-07-10 13:09:46 -04:00
github-actions[bot]
451e58e6d5 chore: sync next package version to 1.7.0-rc.5 2026-07-10 17:09:38 +00:00
Tom Boucher
142b190bd8 Merge pull request #2105 from open-gsd/fix/2056-plan-phase-foreign-prefix
fix(#2056): guard init plan-phase against foreign-prefix numeric collapse
2026-07-10 12:35:13 -04:00
Tom Boucher
a79cdf0f32 Merge pull request #2145 from open-gsd/docs/2143-markdown-table-mutation-adr
docs(#2144): add ADR-2143 markdown table + mutation + fail-loud consolidation (Phase 0)
2026-07-10 12:34:56 -04:00
Tom Boucher
246bcffa43 Merge pull request #2146 from open-gsd/feat/2122-codex-gpt56-model-defaults
feat(#2122): advance Codex/OpenAI model defaults to GPT-5.6 (Sol/Terra/Luna)
2026-07-10 12:34:34 -04:00
Tom Boucher
fed10e331d docs(changeset): backfill PR number (#2146) 2026-07-10 12:23:49 -04:00
Tom Boucher
7bbc243cf5 docs(#2144): index ADR-2143 in docs/adr/README.md
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:12:07 -04:00
Tom Boucher
a146812c5e docs(#2144): add ADR-2143 markdown table + mutation + fail-loud consolidation (Phase 0)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:12:06 -04:00
Tom Boucher
185abe2d66 feat(codex): advance Codex/OpenAI model defaults to GPT-5.6 (Sol/Terra/Luna)
Update runtimeTierDefaults.codex and providerPresets.openai in
model-catalog.json to the GPT-5.6 family (gpt-5.6-sol/terra/luna),
advancing from the superseded GPT-5.4/5.5 generation.

Model IDs verified against OpenAI developer API docs:
- gpt-5.6-sol: flagship, /, reasoning xhigh
- gpt-5.6-terra: balanced, .50/, reasoning medium
- gpt-5.6-luna: fast/cheap, /, reasoning medium

Tier mapping is 1:1 (Sol↔flagship, Terra↔balanced, Luna↔fast),
so profile semantics are unchanged — only the underlying IDs advance.

Updates: catalog JSON, test assertions (catalog defaults), docs
(CONFIGURATION.md + zh-CN/pt-BR translations, workflow settings),
and changeset.

Closes #2122
2026-07-10 12:11:55 -04:00
Tom Boucher
7866e22457 Merge remote-tracking branch 'origin/next' into fix/2056-plan-phase-foreign-prefix
# Conflicts:
#	src/init.cts
2026-07-10 12:01:32 -04:00
Tom Boucher
a890dee7b0 Merge pull request #2141 from open-gsd/chore/2128-phase-id-drift-guard
chore(#2121): phase-id anti-divergence guard + markdown ReDoS hardening — Phase 4
2026-07-10 11:56:22 -04:00
Tom Boucher
27f73cb43f docs(changeset): backfill PR number (#2141)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 11:42:32 -04:00
Tom Boucher
8e4ebb49e4 fix(#2128): address shared-seam review regressions (#557, action-scan, comment-strip)
Final convergence review found the shared <tag> seam introduced 3 behavior
regressions; fixed all + locked with tests:

- #557 REGRESSION: stripTaggedBlocks's attribute-tolerance stripped `<details open>`
  (the ACTIVE-milestone marker) that the old `<details>`-only regex preserved. The
  seam now takes `allowAttributes` (default false) — details/decisions strip is
  attr-INTOLERANT (preserves `<details open>`); only `<task type="…">` opts in.
  Regression test added to roadmap-parser + markdown-sectionizer suites.
- verify.cts actionZones (negative-grep-echo security scan): reverted to a bounded
  to-first-close scan `<action>([\s\S]{0,20000}?)</action>` so a grep-echo trick
  can't hide behind an unterminated inner <action> (the seam's stop-at-next-open
  would drop it). ReDoS-safe via the cap.
- check-command-router HTML-comment strip: `(?:-->|$)` fallback wiped to EOF
  (fail-closed spurious gate block) — replaced with stop-at-next-open so an
  unclosed `<!--` leaves downstream tags intact.
- Updated the extractTaggedBlocks nested-tag tests to the new (stop-at-next-open)
  behavior: `<x><x>inner</x></x>` -> ['inner'].

All vectors still linear; every fix verified in-process.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 11:12:45 -04:00
Tom Boucher
01b691fae8 fix(#2128): route <tag> scans through a hardened shared seam (root-cause ReDoS fix)
A convergence audit showed the `<tag>[\s\S]*?</tag>` lazy-scan ReDoS was pervasive
(a dozen+ bespoke copies across roadmap-parser/check-command-router/verify), each
a distinct quadratic vector on a large document with unclosed tags. Rather than
whack-a-mole, single-source them (maintainer-directed):

- markdown-sectionizer: extractTaggedBlocks now shares one ReDoS-safe
  `taggedBlockPattern` (stop-at-next-open, bounded optional attributes) and gains
  a `stripTaggedBlocks` companion for block removal.
- roadmap-parser: 3 `<details>` strips -> stripTaggedBlocks (behavior-identical —
  no <details> here carries attributes).
- verify: actionZones + both <task> loops + their nested <name>/<files>/gate/req
  extractions -> extractTaggedBlocks (behavior byte-equivalent, verified).
- check-command-router: the objective|tasks?|action alternation hardened in place
  (distinct multi-tag shape); HTML-comment strip gains a `$` fallback.

Every vector now linear (<3ms on 1.5MB adversarial); real content unchanged
(end-to-end verify/roadmap resolution + task extraction confirmed). The only
remaining `<!--…-->` scan (uat.cts:201) is anchored + non-global — one scan, safe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:42:18 -04:00
Tom Boucher
2f6662d195 fix(#2128): bound the remaining lazy-scan ReDoS vectors (files_modified, Plans-count, <tag>)
A ReDoS-completeness audit surfaced a distinct class beyond the tag/bracket
clause: unbounded `[\s\S]*?` / `[^\]]*` lazy-scans searching for a literal
terminator that may never appear, driven quadratic by REPEATED structures in a
large PLAN.md/ROADMAP.md. Folded all 7 in at maintainer direction:

- files_modified `[^\]]*` -> `[^\]]{0,8000}` (commands.cts, verify.cts): 39.7s -> 0.9s.
- Plans-count `[\s\S]*?` -> section-local `(?:(?!\n#{1,4}\s)[\s\S])*?` — stops at the
  next heading (semantically correct: Plans: belongs to the phase's own section)
  (roadmap.cts x3, phase.cts): 36s -> 4ms.
- <tag> extraction `([\s\S]*?)` -> stop at the next same-tag opening
  `((?:(?!<tag>)[\s\S])*?)` (verify.cts x3, markdown-sectionizer.cts): ~6s -> 2ms.

Every vector is now linear (comprehensively re-measured); real content matches
(end-to-end `roadmap get-phase` still resolves Plans-counted phases). Pre-existing;
byte-behavior preserved for realistic inputs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:11:43 -04:00
Tom Boucher
b321bc04f4 fix(#2128): bound the sibling bracket-prefix clause — complete the ReDoS fix
Review caught that the prior commit bounded only the paren tag clause and left
the SIBLING bracket-prefix `(?:\[[^\]]+\]\s*)?` (same host regexes, before Phase)
UNBOUNDED — the identical quadratic reachable via a `[...]` run (measured ~16s at
1.7MB). Bound `[^\]]+`/`[^\]]*` -> {1,200}/{0,200} across all 19 phase/milestone
heading prefixes. Comprehensive re-measurement now shows EVERY vector linear
(bracket/paren/id/name/milestone all ~2-44ms at 2.45MB; bracket scaling
2k->2ms, 4k->5ms, 8k->10ms). Also: update the #1729 literal-mirror parity test
off its stale unbounded constant, and add limit-1 (199) boundary coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 09:44:49 -04:00
Tom Boucher
c1cd43a39f fix(#2128): bound the phase-tag clause to {0,200} — kill quadratic ReDoS
The canonical OPTIONAL_PHASE_TAG_SOURCE tag clause `(?:\s*\([^)\n]*\))?` (and its
inlined literal mirrors across 11 modules) had an UNBOUNDED body, making the
optional-group + /g header scan quadratic on adversarial ROADMAP.md/STATE.md — a
long run of `(` after a header ran ~18.8s at 1.7MB. Bound the body to {0,200} in
the constant AND every mirror in lockstep (the #1729 "both forms change together"
contract), so the scan is linear: the same 1.7MB input now resolves in ~9ms
(measured), while real tags (a handful of chars) still match and a 201-char tag
is rejected. Added a #2128 boundary regression to the #1729 suite.

Pre-existing (byte-identical before/after the Phase 4 migrations); folded in at
maintainer direction rather than deferred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 09:31:17 -04:00
Tom Boucher
a1de52d71b fix(#2128): sanctions must be a dedicated // comment line (decoy-proof)
Re-review found the `// phase-id-owner:` suppression treated a `//` embedded in a
string literal as a comment — help/doc text quoting the sanction syntax (the exact
string the scanner's own main() prints) would silently suppress a real
re-derivation. Require the marker to LEAD its own comment line (`^\s*//…`), so a
`//` inside a string or trailing a code line never counts. All 5 real sanctions
are already dedicated lines (scanRepo stays green); trailing same-line sanctions
are no longer honored — put the comment on the line directly above.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 09:24:49 -04:00
Tom Boucher
e2eaa5b046 fix(#2128): address review — migrate 9 mis-allowlisted sites, harden scanner + guards
Correctness review of the Phase 4 guard found the allowlist over-broad and the
scanner/guards evadable. Fixed all findings:

- Migrate 9 sites that were wrongly sanctioned: their regex is the PURE canonical
  token (`\d+[A-Z]?(?:\.\d+)*`, no variant), byte-identical to already-migrated
  siblings. The old justification argued against swapping to the extractPhaseToken()
  FUNCTION (behavior-risky) — but the guard only wants the same regex built from
  the SOURCE string (byte-equal, zero risk). Coverage is now 32 migrated / 5
  sanctioned, not the overstated 23 / 14 (audit.cts x3, uat.cts, init.cts x4,
  roadmap-upgrade.cts). Each conversion proven byte-equal (.source + .flags).
- Harden the drift detector: also catch the `[0-9]`-in-place-of-`\d` variant;
  document the accepted limits (cross-line split, semantic restructuring —
  covered by the identity guard + review, not a text scan).
- Sanction robustness: a `phase-id-owner:` marker now counts only inside a `//`
  comment (a bare substring in a string no longer suppresses a real flag), and
  the preceding-line window skips blank lines (an auto-formatter's blank line no
  longer reactivates the flag).
- roadmap-parser.cts:462 comment: corrected — that regex carries no /i flag, so
  its [A-Za-z] class does real case work (matches state.cts:1409's rationale).
- Identity guard: surface require failures instead of silently skipping, and
  floor coverage at >75% of consumer modules (inspects 156/157).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 09:14:15 -04:00
Tom Boucher
dfad3a7510 refactor(#2128): single-source 23 phase-token re-derivations; sanction 14 context-specific sites
Route 23 literal re-derivations of the canonical phase-number token through
phase-id.cjs `PHASE_NUMBER_TOKEN_SOURCE` (via new RegExp). Each conversion was
proven BYTE-IDENTICAL (old.source === new.source && old.flags === new.flags), so
the runtime regexes are unchanged — zero behavior change by construction.

The remaining 14 phase-token sites are genuine but context-specific and stay
literal with a `// phase-id-owner: <reason>` sanction: dir-name parses whose
dash-continuation semantics differ from extractPhaseToken, and the [A-Za-z]
case-variant / [.-] dot-or-dash separator forms that are not source-byte-equal
to the canonical token.

Scanner (`npm run check:phase-id-drift`) is now green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 08:49:36 -04:00
Tom Boucher
09be501eb7 feat(#2128): phase-id anti-divergence guard — canonical token source + drift scanner + guards
Phase 4 of epic #2121 (ADR-2121 Decision 7), closing the recurrence loop that
produced #2111 / #2114 / #2104: no module outside src/phase-id.cts may
re-implement phase-ID parsing without failing CI.

- phase-id.cts: add PHASE_NUMBER_TOKEN_SOURCE — the canonical phase-number-token
  grammar (\d+[A-Z]?(?:\.\d+)*) for enumeration/scan call sites, the ANY-phase
  counterpart to phaseMarkdownRegexSource(n)'s known-number lookup. Extend-only
  (never touches normalizePhaseName; blast radius 79 fns / CRITICAL).
- scripts/lint-phase-id-drift.cjs: pure findPhaseIdRegexDrift(text) + scanRepo(root),
  wired to `npm run check:phase-id-drift`. Flags a literal re-derivation of the
  canonical token (both /\d/ and new-RegExp `\\d` escaping, plus the [A-Za-z] and
  [.-] near-variants) anywhere in src/** outside phase-id.cts, unless sanctioned
  with `// phase-id-owner: <reason>`. Narrow by design: bare \d+, digits-only
  captures, \w ids, status-message text and pipe-tables are not flagged.
- tests/phase-id-drift-guard.test.cjs: fail-first drift cases (AC1) + live
  scanRepo(ROOT) zero-drift (AC3) + identity guard — phase-id.cjs exports the
  complete locked surface and no consumer re-exports a divergent copy (AC2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 08:49:36 -04:00
Tom Boucher
3283ce5ae7 Merge pull request #2139 from open-gsd/chore/2126-migrate-roadmap-lookup
chore(#2121): route roadmap.cts through shared lookup sources (drives #2114) — Phase 3
2026-07-10 07:55:00 -04:00
Tom Boucher
067bbac8fa docs(changeset): backfill PR number (#2139)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 07:41:36 -04:00