Skip to content

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 03 Aug 16:09
· 17 commits to main since this release
b21abf3

Added

  • Canonical distribution identity. The product and Claude/Codex/Gemini
    plugins remain prd-spec-generator, while the official MCP Registry entry
    and primary portable bundle become ai-architect-mcp-spec at version 0.7.0.
    Repository-facing branding and URLs move to AI Architect MCP Spec at
    cdeust/ai-architect-mcp-spec.
    Releases retain prd-spec-generator.mcpb as a byte-identical compatibility
    asset, and the existing prd-gen server/tool namespace remains unchanged.
    The release procedure deprecates all versions of the former Registry entry
    only after the new canonical entry is active.

  • Portable Spec Verifier for Codex and Gemini CLI. Both host manifests now
    launch the same opt-in verifier profile, which advertises and accepts only
    validate_prd_section and validate_prd_document. The shared audit-prd
    and validate-spec skills preserve the boundary between deterministic
    structural conformance and semantic or factual correctness. The existing
    Claude manifest still selects no profile, so its default 17-tool full
    surface is unchanged. Distribution tests pin manifest versions, launch
    arguments, supported skill frontmatter, and that Claude compatibility
    invariant.

Fixed

  • Registry checksum guard now validates the field consumers actually read.
    Release automation and verification use the schema-defined fileSha256
    property rather than agreeing circularly on an unused file_sha256 field.
    The checksum is omitted until the artifact exists, and both identity and
    digest guards explicitly reject the former all-zero placeholder.

  • Codex and Gemini verifier startup from immutable plugin installs. Their
    host manifests now execute the bundled server directly instead of running a
    first-launch npm ci inside the installed plugin directory, which Codex
    mounts read-only. Ajv is statically imported so esbuild carries it in the
    bundle, and the portable-host smoke test makes the staged plugin root
    read-only before exercising MCP initialize, tool discovery, and validation.
    Claude Code remains the primary full-profile interface: its .mcp.json
    launch path and 17-tool surface are unchanged and covered by regression
    tests. The obsolete verifier-only dependency branch was removed from the
    remaining Claude launcher after the portable hosts stopped invoking it.

  • The server advertised the wrong version to every host that connected.
    serverInfo.version was the literal 0.4.0 in packages/mcp-server/src/index.ts
    while package.json, .claude-plugin/plugin.json, manifest.json and server.json
    all carried 0.6.1 — three releases of drift, read by every MCP client at
    handshake and by the registry entry built from it. The number is no longer
    written down twice: server-version.ts resolves it at startup from the
    package.json that ships beside the running bundle (the plugin tree and the
    staged .mcpb both carry mcp-server/package.json), falling back to the root
    package.json for workspace runs, and returning an obviously-unresolved
    sentinel rather than a plausible-looking number if neither can be read.
    pnpm bundle stamps mcp-server/package.json from the root version
    (scripts/stamp-bundle-version.mjs), and CI's bundle-freshness check now
    diffs all of mcp-server/, so an unstamped commit fails.

    The gate that should have caught this is fixed too: smoke-mcpb.sh printed
    serverInfo.version in its OK line while asserting only that a serverInfo
    existed, so the wrong version passed CI in green for three releases. It now
    asserts the advertised version equals the one manifest.json declares — two
    independent mirrors of the release, so the check can actually fail.

Added

  • MCP prompts capability (#28): prompts/list + prompts/get publish the
    pipeline ordering as enumerable protocol — run_prd_pipeline(context, request)
    (coordinate_context_budget → start_pipeline → get_pipeline_state →
    submit_action_result → plan_document_verification → conclude_verification) and
    verify_prd_document(run_id). Each step's one-line summary is pulled from the
    live registered-tool description (the same schema tools/list advertises), so
    the ordering is not hand-copied a third time — packages/mcp-server/src/mcp-prompts.ts.
  • MCP tool profiles (#28): full/agent profiles (tool-profiles.ts)
    selected by --profile / PRD_GEN_PROFILE. agent advertises the 12
    agent-facing generation/verification tools; full exposes all 17 including the
    internal diagnostics (get_config, read_skill_config, check_health,
    get_quality_history, get_strategy_effectiveness). Per-profile initialize
    instructions.
  • resources/list interop shim (#28): the server now answers resources/list
    and resources/templates/list with empty arrays and declares the resources
    capability, so clients that probe resources regardless of declared capabilities
    do not surface -32601 as a failed connection (CBM upstream #958). Rationale
    recorded at the use site in index.ts per §8.

Security

  • Every known-vulnerable dependency is gone, and the audit ignore list is now
    empty
    (#36, Scorecard VulnerabilitiesID). The tree carried 39 advisories
    (1 critical, 12 high) and pnpm.auditConfig.ignoreGhsas suppressed 8 of them.
    Both are now zero: pnpm audit reports {critical:0, high:0, moderate:0, low:0} with nothing suppressed. Floors come from each advisory's
    first_patched_version, not from guesswork — vitest (critical
    GHSA-5xrq-8626-4rwp, packages/benchmark was pinned at ^2.0.0 while the
    rest of the repo ran ^4), vite 8.1.5, postcss 8.5.23, hono 4.12.32,
    @hono/node-server 2.0.5, fast-uri 3.1.4, ip-address 10.3.1, qs 6.15.3,
    body-parser 2.3.0, js-yaml 4.3.0, esbuild 0.28.1, mathjs 15.2.0.
    Transitive floors are pinned via pnpm.overrides, each satisfying its
    declaring parent's own range (@modelcontextprotocol/sdk is already at its
    latest 1.29.0, so there was no upstream release to wait for).
    The previous deferral said mathjs was "absent from the shipped .mcpb"; that
    was false — grep -c mathjs mcp-server/index.js returns 730 on the bundle it
    described — so the two-major bump was owed rather than optional.

  • The plugin's runtime provisioning now verifies integrity hashes
    (#36, Scorecard PinnedDependenciesID). bin/ensure-deps.sh ran npm install --no-package-lock on the user's machine at first launch, so the shipped
    plugin re-resolved ^8.17.1 to whatever it meant that day, unverified. It now
    runs npm ci against a committed mcp-server/package-lock.json (44 of 45
    entries carry an integrity hash). Scorecard's shell checker accepts exactly
    this one form: isNpmUnpinnedDownload treats a command as pinned only when it
    contains ci, so pinning versions inside npm install pkg@1.2.3 satisfies
    neither the checker nor the actual threat.

  • Least-privilege GITHUB_TOKEN across CI (#36, Scorecard
    TokenPermissionsID): ci.yml declared no top-level permissions: block.
    Per Scorecard's own checks/evaluation/permissions.go, that undeclared
    top-level is what zeroed the check; release.yml's job-level contents: write — which creating a GitHub Release genuinely requires — costs nothing
    because that file already declares contents: read at top level.

Added

  • Property-based tests for validateSection (#36, Scorecard FuzzingID):
    six contract invariants under fast-check — never throws, score stays in
    [0,1], rulesPassed/violations partition rulesChecked,
    hasCriticalViolations agrees with the violation set, determinism, and
    section-type echo. The function is fed LLM output, so its input space is "any
    string a model might emit"; the two defects regex-hardening.test.ts records
    ([:<≤<=] never matching <=, and test_foo matching inside mytest_foo)
    were both reachable by ordinary inputs nobody had written down.

  • Dependabot (#36, Scorecard DependencyUpdateToolID) for npm and
    github-actions. The second ecosystem matters as much as the first: every
    uses: is pinned by commit SHA, and a SHA pin never ages out on its own, so
    without it the repo trades a supply-chain risk for an unpatched-action risk.

Fixed

  • The .mcpb bundle could not start. manifest.json declares
    server.mcp_config = node ${__dirname}/mcp-server/index.js, and the staged
    tree carried no node_modules: launching it exited immediately with
    Cannot find module 'ajv'. bin/ensure-deps.sh shipped inside the bundle but
    nothing in the .mcpb ever invoked it — that launcher belongs to the plugin
    path (.mcp.json), which passes it explicitly. The .mcpb now ships with its
    runtime dependencies already provisioned from the committed lockfile
    (--omit=optional leaves out the platform-specific better-sqlite3, whose
    absence is the already-declared Beta(7,3) prior fallback).

    The reason this survived a green suite is that the suite exercises the
    workspace sources; nothing ever started the artifact users install. So
    staging moved out of release.yml into scripts/release/stage-mcpb.sh, and
    scripts/release/smoke-mcpb.sh stages the bundle and speaks MCP to it over
    stdio, asserting initialize returns a serverInfo and tools/list returns
    17 tools. It runs as the mcpb smoke CI job on every push and as a gate
    in release.yml before packing. Verified to fail on the defect it exists to
    catch: with provisioning removed it reports SMOKE FAIL: no response to initialize — the server did not start.

    Both channels are now verified end-to-end from a clean tree: the plugin path
    (ensure-deps.shnpm ci → 44 packages) and the .mcpb path both reach
    initialize OK → prd-gen 0.4.0, 17 tools.

  • The ReDoS growth-ratio assertion no longer fails on an unchanged tree.
    expectSubQuadratic timed every small sample and then every large one, so
    ambient-load drift between the two blocks landed entirely in the numerator —
    on a 90-file parallel suite that is routine, and main produced ratio 3.42
    against a 2.5 ceiling on one run while passing the next two. The pair is now
    timed back-to-back and the median is taken over per-pair ratios, so the load
    term is common to numerator and denominator and cancels. The assertion keeps
    its 2.5 ceiling and its power: measured against an injected O(n²) worker it
    still reports 4.00 idle and 3.81 under eight competing CPU spinners, versus
    0.03/0.05 for a linear one.

  • The external-judge harness no longer reads any file on the path to the
    network
    (js/file-access-to-http, the last CodeQL alert open on main).
    Both inputs used to be paths chosen at run time — prompt_source named a
    file inside the claim data, and judge.mjs --prompt-file named one on the
    command line — so the corpus was substitutable: point either at another file
    and its bytes are posted to a third-party LLM API. #37 guarded the first with
    a traversal check; this removes both reads instead. The corpus is now bound
    by a static import … with { type: "json" }, AC-008's historical text is
    inline in the fixture (its file recorded in evidence_source as provenance,
    pinned byte-for-byte by a test), and judge.mjs reads stdin — < prompt.txt
    is the same invocation with the shell doing the open. Verified with the
    CodeQL CLI against the query's own model: 1 result before, 0 after, and 0 new
    alerts across the full security-and-quality suite.

  • Three polynomial-ReDoS patterns closed in the hard-output rules
    (js/polynomial-redos, the two CodeQL alerts that survived #37). All three
    were reported at the shared findPatternViolations call site but lived in the
    patterns handed to it: sp_not_in_fr_table's cell scan, and
    no_placeholder_tests' TODO-body and matrix-row patterns. Each measured
    3.9x–4.0x per doubling before the fix — 2.6 s on a 176 KB single-line input —
    and is now linear. Pinned by growth-ratio tests (not wall-clock thresholds) in
    packages/validation/src/__tests__/regex-hardening.test.ts, each of which
    fails on the pre-fix code.

  • Internal diagnostics tools are gated, not merely hidden under the agent
    profile (#28 criterion 5): an excluded tool is absent from tools/list AND
    rejected on call (RegisteredTool.disable()-32602 "Tool … disabled").
    Hiding from the list while still executing would be a hole, not an
    optimization. Asserted by packages/mcp-server/src/__tests__/mcp-prompts.test.ts.

Removed

  • judge.mjs --prompt-file — prompt text now comes from stdin only.
    node judge.mjs … < prompt.txt is byte-for-byte the same invocation with the
    shell performing the open, so no capability is lost; what goes is a second
    way to do one thing that happened to be the exfiltration primitive above. No
    caller in the repo used the flag.

  • prompt_source in fixtures/ground-truth.json, replaced by the inline
    evidence field every other claim already used, plus an evidence_source
    provenance pointer that nothing reads. resolveClaimEvidence therefore no
    longer touches the filesystem, and the resolveInsideFixtures containment
    helper added in #37 is gone with the read it guarded.

Changed

  • sp_not_in_fr_table is now row-scoped (consequence of the ReDoS fix
    above; the old pattern had no linear equivalent). \s and [^|] both match
    \n, so the old greedy cell loop ran to the LAST Story-Points cell in the
    section and emitted ONE violation whose evidence spanned every row in
    between. Two offending rows now produce two violations, each carrying its own
    row as offendingContent — so a section with N offending rows scores N
    penalties where it previously scored one.

  • no_placeholder_tests matrix-row detection is line-scoped. For the same
    reason (\s matching \n), a // TODO on the line after a matrix row was
    read as that row's third cell. A markdown row cannot wrap, so this removes a
    false positive rather than a detection.

  • The default MCP tool profile is full (behaviour preserved). This diverges
    from #28 criterion 3's "default to the agent-facing set": shrinking the
    default advertised surface is a breaking change (a client that called a
    now-hidden tool would break), so — mirroring automatised-pipeline's
    ToolProfile and this parity wave's decision across all three repos — full
    stays the default and agent is opt-in. No default behaviour change ships, so
    manifest.json / server.json need no default update; the opt-in env
    (PRD_GEN_PROFILE) is documented here.

  • Public-readiness baseline: LICENSE (MIT, sole independent author),
    CONTRIBUTING.md, CODE_OF_CONDUCT.md, SECURITY.md.

  • GitHub issue templates (bug / feature / audit-finding) and PR template
    with audit-cycle checklist.

  • GitHub Actions release workflow (release.yml): tag-triggered build +
    test + bundle-freshness gate + CHANGELOG-driven release notes.

  • assets/banner.svg — ANSI Shadow project banner matching the ai-architect
    ecosystem's visual contract.

  • README cross-links to companion projects (Cortex, zetetic-team-subagents,
    automatised-pipeline).

  • .claude-plugin/marketplace.json and rewritten plugin.json — marketplace
    distribution via claude plugin marketplace add cdeust/prd-spec-generator.

  • mcp-server/index.js — reproducible ESM bundle (esbuild; better-sqlite3
    stays external); pnpm bundle script; CI bundle-freshness gate.

  • pnpm verify end-to-end chain: install → build → bundle → test.

Note (2026-07-17, #22 backfill): these bullets predate the [0.2.1]
boundary below — they shipped alongside the ## [0.2.0] version-bump
commit (6c41cb7) whose own dedicated section further down this file
already describes a different set of changes (Phase 4 strategy-wiring).
Reconciling [0.2.0]'s content with what 6c41cb7 actually shipped is a
separate, [0.2.0]-scoped correction outside #22's 0.2.1–0.5.0 backfill
range, so this residual is left in place rather than reattributed on
unverified inference.

Canonical .mcpb bundle

Artifact: ai-architect-mcp-spec.mcpb (legacy alias: prd-spec-generator.mcpb)
SHA-256: 6dcf5096089e80dadaeec78743f562ecc6d3a3a5349e7e377275ffa50f20dec7