Skip to content

v0.23.0

Choose a tag to compare

@wfukatsu wfukatsu released this 09 Aug 01:08
· 174 commits to main since this release

The product→architect handoff becomes a contract the code enforces rather than prose it describes. All three plugins (product, architect, scalardb) move to 0.23.0 together.

Added

  • The ID prefix namespace is declared, not just described. Each phase in both
    skill-dependencies.yaml files now carries id_prefix, making the manifests the registry
    of which skill mints which ID prefix. The prefixes previously existed only in each SKILL.md's
    prose, where nothing could detect a collision or an omission — and three skills turned out to
    declare none at all. tools/lib/pipeline_status_data.test.py now asserts that every skill
    which appends to work/traceability.json declares a prefix, that its SKILL.md actually uses
    it, and that no two skills in one manifest claim the same one (NFR- is the single
    deliberate cross-manifest claim — the §1.5 carry-over, asserted as such).
  • Registry phase entries name their pipeline. Every entry in
    work/pipeline-progress.json now carries "plugin": "product" | "architect", written by
    init-output and by each orchestrator on its in_progress stamp. One registry serves both
    pipelines and keys phases by bare name, so for the four names both manifests define this
    field is the only thing that says whose entry it is. tools/nexus-status.sh reads it to
    settle the question outright — an entry labelled for the other pipeline is not this phase's
    status, whatever it says — and falls back to output corroboration where the field is absent.

Fixed

  • A ScalarDB-free project no longer sits at 3/4 outputs forever. The architect manifest listed
    scalardb-applicability.md among define-requirements' unconditional outputs, but the skill
    writes it only when ScalarDB is in play — so on a ScalarDB-free project the output bar could
    never fill and the phase read as unfinished rather than as one with nothing left to write. The
    manifest gained conditional_outputs ("<condition>:<path>"), and the dashboard counts such an
    output only when the project's options satisfy its condition.
  • The validation gate now says whose it is. The gate is the product pipeline's, and it is
    deliberately surfaced on the architect tab as well — requirements resting on an unvalidated
    premise is exactly what an architect wants to know — but an unlabelled gate: no-go printed over
    the architect tree read as architect's own verdict. It now renders as Product gate: no-go
    everywhere except the product view.
  • Handoff detection matches files, not directories. /product:init-output creates
    reports/01_ux/domain-stories/ and reports/02_spec/ui-mocks/ empty, so a directory-existence
    test reported a product handoff on any initialized product project, whether or not a phase had
    ever run. Corrected in /architect:start, /architect:pipeline, define-requirements,
    AGENTS.md and OMNIGENT.md; define-requirements additionally states which product artifacts
    it found and which were absent, since a partial product run changes what can be carried over.
  • AGENTS.md and OMNIGENT.md now document the handoff they were driving. The repo runs the
    same skills under three orchestrators and requires their entry docs to stay in sync, but only
    CLAUDE.md mentioned the product→architect handoff at all — Codex and the omnigent loader were
    given no detection rule, no artifact mapping, and crucially no statement that
    pipeline-progress.json, traceability.json and context.md are shared by both pipelines and
    must be written additively. Both now carry the detection glob, a pointer to the docs/design.md
    §1 contract, the per-file additive rules including the plugin stamp and the four ambiguous
    phase names, and adapt-change's report-and-stop boundary. CLAUDE.md gained the same
    shared-state paragraph.
  • Three skills wrote nodes nothing downstream could cite. research-landscape,
    generate-ui-mock and generate-frontend all appended to the trace graph without saying
    under which ID prefix, which broke two chains for real: /product:adapt-change --type=market
    seeds its blast radius from market-landscape nodes that had no ID to seed from, and the
    journey → story → screen → feature chain had no screen ID to run through. They now mint
    MKT-, SCR- and PG- respectively, and define-features cites the SCR- each FEAT-
    comes from — so a FR- derived downstream traces all the way back. A generated React
    component creates no node of its own: it is the implementation of a design-system CMP- and
    is recorded on that node instead of being duplicated under a second ID.
  • Token cost is no longer merged across the pipeline boundary. work/token-usage.json was
    keyed by bare phase name like the registry, so the product and architect spend on
    map-domains (or design-api / create-domain-story / report) accumulated in one bucket
    that neither view could claim. hooks/record_token_usage.py now records those four under
    <plugin>:<phase>, taken from the registry entry's plugin field; the dashboard charges a
    bucket only to its own pipeline, leaves the neighbour's to its own tab, and reports a legacy
    un-namespaced bucket as unassigned rather than to whichever tab happens to be open. Every
    other phase name is recorded bare, as before.
  • /product:adapt-change stops at the architect boundary instead of leaving it undefined.
    After a handoff the trace graph holds architect's nodes, so the blast-radius closure reaches
    them by design — but the skill said nothing about what to do with them. It now splits the
    confirmed impact set by node ownership, re-runs only the product side, and writes an
    ## Architect-Side Impact section naming each affected FR- / NFR-, the skill that owns
    it and the command to act on it. It never rewrites an architect artifact: a product-side
    change is grounds to revise the product spec, not authority to rewrite requirements that
    backlog items and shipped code depend on. docs/design.md §7.5 is the new contract, and
    §7.2 no longer implies the re-run crosses over.
  • /architect:pipeline detects a product handoff over the same reports define-requirements
    reads
    — the same glob mismatch already fixed in /architect:start.
  • init-output no longer discards the other pipeline's state. Both /architect:init-output
    and /product:init-output are now explicitly additive: they merge into an existing
    work/pipeline-progress.json instead of re-registering every phase as pending, keep the
    options already set (notably the output_language the user chose), and create
    work/context.md / work/traceability.json only when absent. On the product→architect
    handoff /architect:start runs init-output immediately before
    /architect:define-requirements — and init-output used to create work/context.md "as an
    empty file", erasing the product-side Open Questions table that define-requirements reads
    in its very next step. /product:init-output likewise no longer truncates
    work/traceability.json, which is the single cross-plugin trace graph architect appends its
    FR- / NFR- nodes to (docs/design.md §1.5).
  • A phase name both pipelines define is no longer read as done on one pipeline's word.
    map-domains, design-api, create-domain-story and report are defined by both
    manifests, and the progress registry keys phases by bare name — so a product phase
    recorded completed rendered as the architect phase being complete, and
    /architect:pipeline --resume-from would have skipped it. tools/nexus-status.sh now
    trusts such an entry only when the phase's own declared outputs exist to corroborate it,
    and otherwise derives the status from the filesystem and reports shared-name drift; a
    running phase (in_progress) and a skip the project actually asked for are exempt.
    skills/common/progress-registry.md states the same rule for the orchestrators — confirm
    an ambiguous entry against the outputs on disk before treating it as satisfied — plus the
    additive-write rule the shared registry requires. init-output additionally records each
    such entry in warnings[].
  • /architect:start detects a product handoff over the same reports define-requirements
    reads.
    Detection globbed only reports/02_spec|03_domain|04_quality, so a product run
    that stopped early (--profile=mvp writes only reports/00_core/) could be announced as
    having no product artifacts by the skill about to consume them. The two sets are now
    identical.

Changed

  • Open Questions are asked, not filed. Every skill that could write TBD now runs the
    protocol in the new rules/open-questions.md: an unknown it cannot resolve from its own
    inputs is put to the user with AskUserQuestion — 2–4 candidate answers the skill derived
    from context, each described by what it changes downstream — and only what the user defers,
    cannot answer in-session, or was never asked (--auto) becomes a TBD. Previously an
    unknown went straight to "record it as TBD in Open Questions", so a question the user
    could have answered in one click was instead deferred into a report nobody re-opened.
  • Anything the options cannot express is answered in free text. Skills never author an
    "Other" option (the harness appends one, and that is the free-text path) and never round a
    free-text answer to the nearest option — it is recorded verbatim and marked as free text,
    with only units/IDs normalized and the normalization echoed back. Inherently free-form
    answers are asked as representative bands (p95 < 100 ms / < 500 ms / < 1 s) so the
    exact figure arrives through "Other", or in prose when no meaningful bands exist — never
    skipped to TBD because the answer would not fit a menu.
  • What stays open now says why. Open Questions entries carry an OQ- ID, a status
    (answered / deferred / unasked / external), the answer, the options offered, an
    owner and the downstream impact; a TBD in an artifact carries its question ID
    (TBD (OQ-012)). /product:report groups the header by status so a question nobody was
    asked is visibly different from one the user consciously deferred, and /product:review
    reports an unasked TBD as a finding. --auto runs record the question and the options
    that would have been offered
    , so a later pass can answer instead of re-deriving.
  • Questions carry across phases. Each skill picks up the deferred / unasked entries in
    its own domain at its read-context step, re-asks them in its first question batch, and
    updates them in place under the same OQ- ID — no duplicates, and nothing already answered
    is re-asked. /product:init-output seeds work/context.md with the ## Open Questions
    table; the product→architect handoff carries the IDs into
    reports/00_requirements/open-questions.md.
  • Wired through CLAUDE.md, AGENTS.md and OMNIGENT.md: Codex and the omnigent loader have
    no harness-appended "Other", so they print an explicit "or type your own answer" line under
    the numbered choices and record a non-matching reply as a free-text answer.
  • The token-cost dashboard now prices its component columns. In the live
    /architect:report-token-cost dashboard the per-model in / out / cache-read /
    cache-write columns hold money ($) instead of token counts — the dashboard is read to
    answer "what did this cost", and the token total is already its own column. b switches
    them back to counts and the bottom bar names the current unit, so the toggle is
    discoverable rather than documented-only. The static and --md report keeps token counts
    (there the columns break down the token total the same table carries); --breakdown= still
    overrides either default.
  • A nonzero cost never renders as $0.0000. Amounts below a hundredth of a cent now show
    as <$0.0001 (<¥1 under --currency=jpy), instead of rounding a real charge down to
    something that reads as free — visible now that the dashboard prices per-component columns,
    where cheap models land in that range.