Spec Studio

TypeScript and Markdown duplicates are both refused

spec:validation.duplicate-ids.dual-carrierExample / Scenario · storyready floor ready

Packs: pack:self-hosting-v1

Design: no design entries · no pinned declarations · no decisions

Findings: no errors, no warnings

specs/validation/duplicate-ids.dual-carrier.sdp.md

No open questions and no blocking questions; implementation binding none, verifier binding present, and expected-outcome oracle none.

Intent
outcome
Execute the duplicate-ID rule across both carrier surfaces.
Behavior

Examples

Given a {firstCarrier: "TypeScript"} carrier declares {specId: "spec:fixture.duplicate"}
Given a {secondCarrier: "Markdown"} carrier declares {specId: "spec:fixture.duplicate"}
When the extraction root is read
Then both sites report {findingId: "extract/duplicate-id"}
Then no graph node is emitted for {specId: "spec:fixture.duplicate"}
Verification

Verifier bindings

  • test:protocol.duplicate-ids.dual-carrier · dual-carrier duplicate-ID contract verifies carrier exclusion · anchored · test anchor binding present · test/self-hosting-duplicate-ids.test.ts:1155

    External run evidence from test/self-hosting-duplicate-ids.test.ts

    Test anchors: test:protocol.duplicate-ids.dual-carrier. Specs verified by those bindings: spec:validation.duplicate-ids, spec:validation.duplicate-ids.dual-carrier.

    Run 2: external run evidence from vitest 4.1.10

    Build: CI 439 · Branch: docs/articles-refresh-1 · Commit: 61576184ea9f39b7f91c51fef5ff991b0bf2d0e6 · Start: 2026-11-04T07:59:39.962Z · Stop: 2026-11-04T08:03:39.928Z

    NameSuiteResultDuration
    names self-hosting generation before collecting its filtered tracergenerated contract preflightpassed579.01 ms
    names checkout generation before collecting its filtered examplegenerated contract preflightpassed480.2 ms
    names both generations when an unfiltered suite lacks both contract treesgenerated contract preflightpassed427.82 ms
    matches a Vitest substring filter before collecting the self-hosting tracergenerated contract preflightpassed667.41 ms
    matches a ./-prefixed filter before collecting the self-hosting tracergenerated contract preflightpassed437.58 ms
    matches an absolute-path filter before collecting the self-hosting tracergenerated contract preflightpassed576.87 ms
    reads an option value ["--reporter","verbose"] as no file filter, so the whole suite's contracts are requiredgenerated contract preflightpassed369.9 ms
    reads an option value ["--maxWorkers","1"] as no file filter, so the whole suite's contracts are requiredgenerated contract preflightpassed435.55 ms
    reads an option value ["-t","duplicate"] as no file filter, so the whole suite's contracts are requiredgenerated contract preflightpassed409.28 ms
    matches a file:line filter by its file before collecting the self-hosting tracergenerated contract preflightpassed416.24 ms
    requires every contract tree for `related`, whose source paths name no test file (present: ["examples/checkout-v1/generated/contracts"])generated contract preflightpassed419.34 ms
    requires every contract tree for `related`, whose source paths name no test file (present: ["generated/contracts"])generated contract preflightpassed385.36 ms
    does not require contracts for an unrelated Vitest filtergenerated contract preflightpassed578.46 ms
    proceeds for the unfiltered invocation once both contract trees are generatedgenerated contract preflightpassed600.6 ms
    fails a pooled run that mutates repository-root generated statepooled root generated-state sentinelpassed590.16 ms
    keeps the dedicated CLI pass when an option carries a separate value, and forwards it therepooled root generated-state sentinelpassed648.54 ms
    routes ["--","test/bootstrap.test.ts"] without running test/cli.test.ts in the poolpooled root generated-state sentinelpassed552.46 ms
    routes ["--","--reporter","dot"] without running test/cli.test.ts in the poolpooled root generated-state sentinelpassed681.36 ms
    routes ["related","src/cli/validate-watch.ts"] without running test/cli.test.ts in the poolpooled root generated-state sentinelpassed560.32 ms
    routes ["test/cli.test.ts:120"] without running test/cli.test.ts in the poolpooled root generated-state sentinelpassed399.75 ms
    routes ["test/bootstrap.test.ts","test/cli.test.ts:120:3"] without running test/cli.test.ts in the poolpooled root generated-state sentinelpassed549.71 ms
    routes ["-h"] without running test/cli.test.ts in the poolpooled root generated-state sentinelpassed400.66 ms
    routes ["--version"] without running test/cli.test.ts in the poolpooled root generated-state sentinelpassed470.63 ms
    routes ["--","--help"] without running test/cli.test.ts in the poolpooled root generated-state sentinelpassed546.12 ms
    routes ["--","-h"] without running test/cli.test.ts in the poolpooled root generated-state sentinelpassed492.21 ms
    routes ["--","--","--help"] without running test/cli.test.ts in the poolpooled root generated-state sentinelpassed627.38 ms
    records each pass of a split run writing ["--reporter=junit","--outputFile=report.xml"] as a blob report and merges thempooled root generated-state sentinelpassed646.19 ms
    records each pass of a split run writing ["test/bootstrap.test.ts","test/cli.test.ts","--reporter","html","-t","alpha"] as a blob report and merges thempooled root generated-state sentinelpassed836.8 ms
    refuses coverage in a split run and says no coverage provider is installedpooled root generated-state sentinelpassed509.67 ms
    refuses a split run writing the html report ["--reporter=html"] before any pass while @vitest/ui is missingpooled root generated-state sentinelpassed549.1 ms
    refuses a split run writing the html report ["--reporter","html","--outputFile=report/index.html"] before any pass while @vitest/ui is missingpooled root generated-state sentinelpassed513.14 ms
    refuses a split run writing the blob report ["--reporter","blob"], which Vitest cannot merge intopooled root generated-state sentinelpassed568.34 ms
    refuses a split run writing the blob report ["--reporter=blob","--outputFile=blob.json"], which Vitest cannot merge intopooled root generated-state sentinelpassed468.19 ms
    refuses a split run whose configuration sets reporters: ["blob"]pooled root generated-state sentinelpassed692.5 ms
    refuses a split run whose configuration sets coverage: { enabled: true }pooled root generated-state sentinelpassed544.53 ms
    refuses a split run whose configuration sets reporters: ["html"]pooled root generated-state sentinelpassed461.01 ms
    leaves a configuration Vitest cannot load to the passes, reading the caller's options alonepooled root generated-state sentinelpassed612.78 ms
    hands each pass the caller's environment, untouched by reading the configurationpooled root generated-state sentinelpassed658.73 ms
    leaves each pass its configured reporters when none of them writes a report filepooled root generated-state sentinelpassed568.28 ms
    forwards the listing command ["list","--filesOnly"] once, without running the CLI suitepooled root generated-state sentinelpassed517.21 ms
    forwards the listing command ["--reporter","verbose","list","test/cli.test.ts"] once, without running the CLI suitepooled root generated-state sentinelpassed490.4 ms
    forwards the benchmark command ["bench"] once, with no CLI unit pass and no contract treepooled root generated-state sentinelpassed527.7 ms
    forwards the benchmark command ["bench","test/cli.test.ts"] once, with no CLI unit pass and no contract treepooled root generated-state sentinelpassed583.68 ms
    forwards the benchmark command ["--","bench"] once, with no CLI unit pass and no contract treepooled root generated-state sentinelpassed539.32 ms
    keeps the dedicated CLI pass for the executing command watchpooled root generated-state sentinelpassed630.64 ms
    keeps the dedicated CLI pass for the executing command devpooled root generated-state sentinelpassed577.25 ms
    passes a pooled run that leaves repository-root generated state untouchedpooled root generated-state sentinelpassed479.13 ms
    runs ["-t","cli alpha"] as Vitest would select it, with the CLI suite only in its own passthe test wrapper over real Vitestpassed2241.23 ms
    runs ["--testNamePattern","gamma"] as Vitest would select it, with the CLI suite only in its own passthe test wrapper over real Vitestpassed2067.45 ms
    runs ["--","test/other.test.ts"] as Vitest would select it, with the CLI suite only in its own passthe test wrapper over real Vitestpassed1142.89 ms
    runs ["--","-t","beta"] as Vitest would select it, with the CLI suite only in its own passthe test wrapper over real Vitestpassed2222.36 ms
    runs ["--","--","test/other.test.ts"] as Vitest would select it, with the CLI suite only in its own passthe test wrapper over real Vitestpassed2084.54 ms
    writes one report holding both passes for ["--reporter","junit","--outputFile","report.xml"]the test wrapper over real Vitestpassed2348.31 ms
    writes one report holding both passes for ["--reporter=junit","--outputFile=report.xml"]the test wrapper over real Vitestpassed2563.84 ms
    writes one report holding both passes for ["--reporter=junit","--outputFile.junit=report.xml","-t","a"]the test wrapper over real Vitestpassed2553.09 ms
    writes one JUnit document to stdout when no output file is namedthe test wrapper over real Vitestpassed2786.99 ms
    merges both passes with the configuration file ["--config","custom.config.mjs"] they ran withthe test wrapper over real Vitestpassed2782.51 ms
    merges both passes with the configuration file ["-c","custom.config.mjs"] they ran withthe test wrapper over real Vitestpassed2606.61 ms
    merges both passes into the report file the configuration names with reporters: ["junit"], outputFile: "report.xml"the test wrapper over real Vitestpassed2582.37 ms
    merges both passes into the report file the configuration names with reporters: [["junit", { outputFile: "report.xml" }]]the test wrapper over real Vitestpassed2731.81 ms
    merges both passes into the report file a configuration names when it loads with the runner loader alonethe test wrapper over real Vitestpassed2844.28 ms
    merges both passes into the report file a configuration from a package names through its main filethe test wrapper over real Vitestpassed3109.52 ms
    merges both passes into the report file a configuration from a package names through its exports for import alonethe test wrapper over real Vitestpassed3168.47 ms
    merges both passes into the report file a configuration from a package names through its exports for import, where require names another filethe test wrapper over real Vitestpassed2607.78 ms
    reads a configuration that asks for a report file when process.env.NODE_ENV === "test" as the passes read itthe test wrapper over real Vitestpassed2433.7 ms
    reads a configuration that asks for a report file when process.env.VITEST === "true" as the passes read itthe test wrapper over real Vitestpassed2588.33 ms
    reads a configuration that asks for a report file when process.env.TEST === "true" as the passes read itthe test wrapper over real Vitestpassed2648.22 ms
    reads a configuration that asks for a report file when process.env.NODE_ENV === "production" as the passes read itthe test wrapper over real Vitestpassed2674.49 ms
    reports a failing pooled pass in the merged report and stops before the CLI passthe test wrapper over real Vitestpassed1911.05 ms
    prints usage for ["--","--help"] and runs no testthe test wrapper over real Vitestpassed529.26 ms
    prints usage for ["--","-h"] and runs no testthe test wrapper over real Vitestpassed486.62 ms
    derives the duplicate-ID verifier facts from the bound executable examplenot recordedpassed8248.83 ms
    executes the bound example against a fresh worldTypeScript and Markdown duplicates are both refused (spec:validation.duplicate-ids.dual-carrier)passed8.73 ms
Design view

Requirement drawing

  • Satisfying or referencing units: no recorded binding.
Requirement drawing for spec:validation.duplicate-ids.dual-carrier
The diagram as text
  • spec:validation.duplicate-ids.dual-carrier: TypeScript and Markdown duplicates are both refused · declared Spec
  • spec:validation.duplicate-ids: Duplicate carrier IDs are excluded loudly · declared Spec
  • test:protocol.duplicate-ids.dual-carrier: dual-carrier duplicate-ID contract verifies carrier exclusion · anchored test binding · binding present · test/self-hosting-duplicate-ids.test.ts:1155
  • spec:validation.duplicate-ids.dual-carrier refines spec:validation.duplicate-ids · declared
  • spec:validation.duplicate-ids.dual-carrier verifies spec:validation.duplicate-ids · declared
  • test:protocol.duplicate-ids.dual-carrier verifies spec:validation.duplicate-ids.dual-carrier · anchored
Mermaid source

Copyable source; the Studio draws static SVG.

Preferred and chosen syntax: requirementDiagram; exact refines, satisfies and verifies, with claim words in nodes and per-edge comments.

Decisions

No decision record named.

Runtime

No implementation neighborhood recorded.

Runtime observation: not tracked.

Evidence

Runtime observation: not tracked.

Build attestations: not tracked.

Deployments: not tracked.

Bills of materials: not tracked.

Impact
Relations of spec:validation.duplicate-ids.dual-carrier
  • E2: verifies · declared
The diagram as text
  • spec:validation.duplicate-ids: Duplicate carrier IDs are excluded loudly
  • spec:validation.duplicate-ids.dual-carrier: TypeScript and Markdown duplicates are both refused
  • spec:validation.duplicate-ids.dual-carrier refines spec:validation.duplicate-ids · declared
  • spec:validation.duplicate-ids.dual-carrier verifies spec:validation.duplicate-ids · declared
Mermaid source

Copyable source; the Studio draws static SVG.

Preferred syntax: flowchart. Chosen syntax: labelled flowchart, preserving exact relations, claims and folds.

Rests on

Rested on by

No Spec rests on this Spec.

Relations

Relation and directionOther endClaimReasonExercised keys
refines · outgoingDuplicate carrier IDs are excluded loudly
spec:validation.duplicate-ids · resolves
declaredNoneNone
verifies · outgoingDuplicate carrier IDs are excluded loudly
spec:validation.duplicate-ids · resolves
declaredNoneNone

Bindings

Implementation binding: none

Design reference: none

Verifier binding: present

  • test:protocol.duplicate-ids.dual-carrier · dual-carrier duplicate-ID contract verifies carrier exclusion · anchored · test/self-hosting-duplicate-ids.test.ts:1155 · test anchor

Expected-outcome oracle: none

Runtime observation: not tracked.

Run results: not recorded in the graph.

Findings

None.

Composing (0)

Composing needs the script.