Skip to content

Add /sd:port workflow, Check 8 contract-lint, and e2e harness - #26

Merged
developzoneio merged 25 commits into
mainfrom
develop/v1
Aug 10, 2026
Merged

Add /sd:port workflow, Check 8 contract-lint, and e2e harness#26
developzoneio merged 25 commits into
mainfrom
develop/v1

Conversation

@developzoneio

Copy link
Copy Markdown
Owner

Summary

  • Ship the /sd:port fidelity-first port pipeline (10 phases, 6 gates) plus its supporting pieces: sd-port-fidelity skill, port-extract explorer mode, port spec template/snapshot layout, and the port-parity adjudication gate on sd-reviewer.
  • Add Check 8 cross-file contract lint (scripts/contract-lint.sh/.ps1) with a large fixture suite proving parity between the bash and PowerShell implementations.
  • Add a headless e2e behavioral eval harness (tests/e2e/) and CI workflows (ci.yml, e2e-nightly.yml).
  • Add ## Spawned specs section to feature/bug/refactor/perf spec templates so deferred follow-up work has a place to land.
  • Restructure README (Quickstart path, evidence-first framing), add Ko-fi support badge, and expand CHANGELOG/CONTRIBUTING/docs.

Test plan

  • bash scripts/contract-lint.sh --root . and .\scripts\contract-lint.ps1 -Root . (exit 0)
  • .\tests\contract-lint\run-selftest.ps1 (bash/PowerShell parity fixture suite)
  • .\install\install.ps1 -DryRun / sandbox install round-trip

🤖 Generated with Claude Code

developzoneio and others added 25 commits July 28, 2026 11:49
The engine's shell was heavily verified; its product - the command
prompts, agents and skills - had zero automated verification. Every
relationship between those files was asserted in prose and checked by
human review, and that gap had already shipped statically-detectable
defects.

Check 7 closed the inventory drift class. This closes the contract
drift class: 17 rules across CL0xx reference resolution, CL3xx gate
integrity and CL9xx suppression hygiene, as twin pure-file-ops scripts
that emit TSV and nothing else.

Severity lives only in the manifest registry, so a BLOCK/WARN
divergence between the two implementations is structurally impossible,
and a registry parity guard in each linter exits 2 when the rules it
dispatches and the registry disagree. Gate counts become published
claims: the manifest seeds Check 7 quantities, giving README <-
manifest there and manifest <- disk in CL302, hence transitively
README == disk with no gate parser duplicated into validate.

Two real violations were fixed before the linter landed, so the first
green run is distinguishable from a linter that never fires:
architecture.md listed four items against a count of three, and a
setup gate had no literal STOP.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Makes SW-25's Inputs (required|optional) declarations load-bearing: five
new rules (CL100-CL104) cross-check every commands/*.md agent invocation
against the target agent's declared mode in agents/*.md, in both linter
implementations, with matching fixtures and docs.

Also fixes two real drifted invocations found while building this
(commands/refactor.md missing INVARIANTS, agents/code-explorer.md missing
GITNEXUS_AVAILABLE), and suppresses two intentional scoped-re-plan
exceptions with cited reasons.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Four rules over the agent role contract: CL200 (WARN, promotes to BLOCK
later) an agent with no write tool is instructed to write/append/create;
CL201 (BLOCK) an agent declared read-only in contractLint.readOnlyAgents
grows a write tool anyway; CL202 (WARN) an mcp__* name is absent from
contractLint.knownMcpTools; CL203 (WARN) an agent's own frontmatter
declares a tool its own body never mentions. A write tool is exactly
Write/Edit/MultiEdit - Bash deliberately does not count.

Adds a per-agent tools: frontmatter index and an mcp__* token scan to
both linters, five new fixtures (one per rule plus a false-positive
guard for CL200's verb scan), and fixes a latent CL203 gap in six
existing fixtures' copies of the demo agent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Four new contract-lint rules: CL400/CL401 (hardcoded stack command/language
tokens outside a placeholder, fenced example, or suppression comment),
CL402 (hardcoded absolute filesystem path), and CL306 (HARD gate prose
describing an escape hatch with no contract-lint: allow comment) - the
prose half of CL305 that wave 1 deferred. CL306 deliberately excludes
whatever CL305 already governs so the two rules cover disjoint territory.

The MSSQL/C#/TypeScript violations the ticket described were already fixed
in an earlier commit; the real hits this wave found in the current tree
were legitimate multi-stack heuristics and forbidden-example prose, so
those got contract-lint: allow annotations rather than rewrites.

Nine new fixture cases, docs/contract-lint.md updates, and manifest
vocabulary (stackTokens, gateProseEscapeTokens) mirroring the
overrideOptionTokens precedent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds CL500 (WARN, permanent): a file exceeding contractLint.budgets.<area>Bytes,
ratcheted at today's largest file per scan-scope area. The byte count is
normalized (per-line byte sum, not a raw disk read) so the rule agrees with
itself across a CRLF Windows checkout and an LF Linux CI runner. Two new
fixture cases, a registry-parity fix across 7 older fixture overlays that
had not picked up the new rule id, and a bash 'set -e' fix in the byte-budget
lookup helper that was silently killing the linter on any unbudgeted file.
All three shipped WARN with an explicit "promotes to BLOCK in a
follow-up commit once it has run clean for a release" clause. The
engine tree now has zero findings for all three under both linter
implementations, so the promotion is live. Severity is registry-driven
(specwright.manifest.json), so no rule logic changed; nine fixture
overlays and three expected.json goldens updated to match, plus docs
and CHANGELOG corrected to reflect BLOCK severity.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a tiny, dependency-free Node.js todo-list library as the first non-.NET
project run through specwright end-to-end: pre-scaffolded CLAUDE.md,
constitution, and project-config, plus a real .specs/FEAT-todo-priority/
(spec through verify) driven live through the actual sd-spec-architect,
sd-code-explorer, sd-implementer, and sd-reviewer subagents with real gate
approvals, not hand-authored. Reconciles examples/README.md,
docs/walkthrough.md, and root README.md (Quickstart, compatibility matrix,
docs list) to point at it instead of promising a fixture that didn't exist.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Drives real claude -p sessions against a throwaway fixture copy and
asserts on produced artifacts, not transcript wording - the first
mechanism proving the engine behaves correctly end-to-end rather than
just that its assets reference each other correctly. Verified live:
self-test genuinely detects a neutered spec-gate guard; full-suite run
found 4/5 scenarios green plus a real, documented product gap (Rule 1
stalls /sd:feature's own status transitions under an enforcing
permission mode) - measured cost and the finding are recorded in
tests/e2e/README.md. Reproducible-3x and the Rule 1 fix are tracked as
follow-up rather than folded into this scope.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Check 7's entire vocabulary (docClaims/claimPhrases) is count-based, so a
stale version string had nothing to trip it - ROADMAP.md said 1.3.0 since
before the 1.4.0 release, unnoticed through every green run, and its
Planned section claimed an empty queue while [Unreleased] carried eight
real entries. A new versionClaims array in specwright.manifest.json closes
this: entries compare a captured x.y.z string against the newest dated
CHANGELOG heading rather than a disk-derived quantity. validate.sh/.ps1
gained a matching check, deliberately independent of Check 6's existing
next_header/$nextHeader (those resolve to whatever line sits directly
below [Unreleased], which is a bullet rather than a heading in the normal
state, so reusing them would have passed on bash and silently done
nothing on PowerShell). selftest-docs grows from 6 scenarios to 7.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Installers now resolve the engine version from CHANGELOG.md and write a
specwright-version.txt stamp into every installed area via the existing
copy machinery, so no engine artifact exists without a way to tell which
version wrote it. Check 5/7 validators, install/README.md, and the CI
round-trip account for the stamp with derived (never hardcoded) counts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Phase 0 now reads the installed specwright-version.txt stamp; Phase 1.5
compares it against project-config.json's version and generalizes the
missing-field check into a full template diff, with version as the one
field Apply may write outside the project-specific preserve-list. Fresh
scaffolds stamp the real installed version instead of the template's
literal 1.0.0. Also removes the template's dead $schema URL (no schema
was ever published, and the org name didn't match the real repo) - the
drift-check now flags any leftover key for removal instead of rewrite.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Gate Complexity trips were never recorded as metrics and this repo's real
corpus is n=1 closed spec, not the "n=1 risk" the ticket assumed - so this
lands the measurement machinery (a new gate:"complexity"/split event inferred
from index.md, /sd:status --calibration, judgement-call caveats on the other
five thresholds) and an ADR recording the honest verdict at this corpus size,
rather than manufacturing a calibrated number from one spec.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cross-project port policy promoted into a skill because two agents need
the same rule body: sd-spec-architect authors the deviation table and the
port task blocks, sd-reviewer judges whether a diff hunk is justified.

Defines structural mirror as the default posture, the four-group deviation
allowlist with a required citation per group, five anti-simplification
rules that reach the implementer through task Acceptance rather than
through the skill, completeness conditions for the three gate tables
phrased as counting and matching predicates a gate can evaluate without
judgement, and the closed four-class hunk vocabulary.

Fidelity findings anchor to the port spec's mandatory fidelity acceptance
criterion, already a legal code anchor - sd-severity-taxonomy's Anchors
table is deliberately left untouched, since its rows are keyed by target
and a new row would relax every reviewer task type.

agents/spec-architect.md was the CL500 ratchet-setter and sat at the
ceiling exactly, so budgets.agentsBytes moves 14454 -> 14671 to admit the
two-line wiring. Reviewed growth, not a reflex to a red run.

Consumers land later: SW-38 template, SW-39 extraction, SW-40 diff and
adjudication, SW-41 pipeline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…yout

Adds the authoring half of the port workflow: a PORT-<slug>-<YYYYMMDD>
spec prefix wired through the registry, a port.template.md whose three
fidelity tables reuse sd-port-fidelity's (SW-37) schemas rather than the
ticket's stale prose precedent, a frozen donor-snapshot layout under
04-artifacts/source/ with a MANIFEST.md format, and SL080-SL083 validate
rules. Freezing enumerates literal paths.protected entries since neither
spec-gate hook supports globs, and PORT-prefix hook recognition is
deliberately deferred to the future port pipeline - both documented as
known limitations rather than worked around. No .ps1/.sh file touched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New TASK = port-extract mode on sd-code-explorer produces a fixed
eight-section donor-side contract (Entry surface, Output surface,
Member closure, Complement set, Collaborators, Non-obvious
invariants, Dead paths on this entry point, Precedent conventions),
replacing the prior free-form /sd:explore prose contract whose gaps a
host implementer filled by invention. /sd:explore gains a --port flag
that invokes it and, being the main-thread caller, owns source_commit
capture (dirty-tree aware) and - under --snapshot contract+source -
donor file copying/hashing into a MANIFEST.md matching
sd-port-fidelity's format, keeping the explorer agent itself
read-only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Enforcement half of the port epic: a main-thread-generated parity diff
(04-artifacts/parity/) plus a new port-parity TASK_TYPE on sd-reviewer
that classifies every hunk with sd-port-fidelity's vocabulary, now five
classes (adds overreached - a deviation row exists but the hunk exceeds
what it licenses), and two whole-artifact checks (member completeness,
path conformance). A justified hunk is a PASS and is not written up, so
a real BLOCK cannot drown in accepted diffs. sd-reviewer gains no Bash
and no write tool - the diff must already exist when it is invoked.

The /sd:verify overlap was decided before any checking logic was
written: both new checks stay with the reviewer since neither can be
decided from 00-spec.md/02-tasks.md alone. Rationale in
docs/architecture.md.

Adds examples/port-parity-fixture/ (clean + broken port trees) as the
regression guard, following spec-lint-fixture's run-by-hand convention.
contractLint.budgets.skillsBytes raised 10656 -> 12377 for the skill's
growth; agentsBytes needed no change.

Diff generation and the /sd:port pipeline itself are SW-41, not yet
built - contract-lint reports one new CL101 WARN for the unhosted mode,
documented in docs/troubleshooting.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wires the port epic (SW-37 skill, SW-38 template/snapshot layout,
SW-39 extraction mode, SW-40 parity gate) into one command: bridge/
extract -> freeze -> host survey -> fidelity tables -> pin behavior ->
plan -> execute batched -> justified-diff parity -> close-out.

- New commands/port.md; WORKFLOW_TYPE = port added to sd-implementer
  (neither feature nor refactor was the right constraint set).
- port keyword registered in the prompt-router map (both hook
  implementations) and project-config.template.json.
- Manifest gates.commands/port.md + workflowCommands + docClaims
  wired; skillsBytes ratchet raised 12377 -> 12412.
- Docs: README/CLAUDE/CONTRIBUTING/install counts re-derived (14
  commands, 47 install files); usage.md phase table; architecture.md
  routing line + gate table; 4 new troubleshooting.md entries.
- Fixed stale "port pipeline lands in a later story" cross-references
  in spec-architect.md, sd-port-fidelity/sd-spec-templates/
  sd-retro-lessons skills, port.template.md, port-parity fixture.

Verified: contract-lint 0 block/12 warn (bash+PowerShell agree),
scripts/validate.sh 8/8, contract-lint fixture self-test 31/31,
sandbox install/uninstall round-trip clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Follow-up work discovered mid-spec had nowhere to land except prose,
where it evaporated. Adds the RCA template's reserved-ID table
(Reserved ID | Type | Title | Owner) to the other four spec types,
a close-out prompt (not a gate) in each corresponding command, the
first SUGGEST-severity /sd:spec validate rule (SL090) for a done spec
that names deferred work with an empty table, and documents that a
reserved ID is a placeholder, not a registry entry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The page opened with a feature count and asked the reader to take 330
lines of tables on trust before showing a single line of output.

- Add `## What it looks like` at the top: the real Gate 1 spec-approval
  STOP and the real Gate 3 reviewer verdict (0 BLOCK / 0 WARN /
  5 SUGGEST / 7 PASS, 18/18 tests), both transcribed from the committed
  FEAT-todo-priority run in examples/fixture-project/.
- Add `## How this differs from prompt-level discipline`, naming what
  enforces the discipline: gates halt the phase, spec-gate denies
  Edit/Write at the PreToolUse layer, the reviewer has no write tools,
  and specs are subagent inputs rather than write-ups.
- Merge the two install sections. Quickstart carries the commands inline
  plus a requirements line; the old section becomes `## Install options
  and uninstall` and keeps only the advanced path.
- Shrink the agent and skill tables to a summary plus a link. Tool
  allowlists, the routing map, and the skill catalogue already live in
  docs/architecture.md verbatim.
- Fix the BMAD acknowledgement (it pointed at a placeholder
  https://github.com/), a seven-month-stale compatibility date, and a
  roadmap bullet reading "nothing queued" against a repo shipping v1.5.0.
- Replace the unsourced "~$2-3 per feature run" with a pointer to
  /sd:status, which reports the reader's own cost from their metrics log.
- Surface examples/port-parity-fixture/ and examples/spec-lint-fixture/,
  previously unmentioned anywhere in the README.

The two new prose counts tripped Check 7's undeclared-claim scan, as
intended; both get docClaims entries so they derive from disk rather
than an exclusion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The previous commit fixed the order of the page but not its volume. The
redundancy was concentrated in four places, not scattered.

- Delete `## Architecture highlights`. Three of its four bullets restated
  hard gates, the cost model and stack-agnosticism verbatim from
  `## Why spec-driven?`; its 18-line ASCII diagram already lives in
  docs/architecture.md. The one distinct idea, the three layers, becomes
  a sentence.
- Drop the fifteen `---` rules, ~30 lines spent drawing a line GitHub
  already draws under every h2.
- Stop printing the command and agent names twice. The Features table was
  a table of contents for the two tables directly beneath it; its rows
  stay (they anchor docClaims) but the duplicated cells become pointers.
- Collapse the spec-folder tree to one expanded spec plus a note, merge
  the two install blocks into one, and drop the YAML frontmatter snippet
  that duplicates docs/architecture.md.

No published claim is dropped, so specwright.manifest.json is untouched:
Check 7 still reports 79 claims and Check 8 still reports 0 BLOCK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merging each platform pair into one fenced block saved lines and broke
three things:

- `./install/install.sh --dry-run && ./install/install.sh` ran the
  preview and then installed immediately, so the dry-run output scrolled
  past and was never read. The uninstall block had the same shape, where
  the real run deletes directories. A dry run you cannot read is not a
  dry run.
- PowerShell sat inside a ```bash fence, so a Windows reader copying the
  block got bash errors and no syntax highlighting.
- The reader had to parse a trailing comment on each line to work out
  which one was theirs.

Now four blocks - bash and powershell, install and uninstall - each with
the preview and the real command on separate lines. Costs ~19 lines
against the previous commit; correctness wins.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@developzoneio
developzoneio merged commit d5bb0e9 into main Aug 10, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant