Skip to content

Releases: toshtag/code-pact

v2.0.0 — bounded archive maintenance

18 Jun 14:00
v2.0.0
bd84281

Choose a tag to compare

v2.0.0 — bounded archive maintenance. state archive-maintain is the one command that keeps .code-pact/state/archive bounded — it recovers any pending delete-intent journal, compacts the loose tail into bundles, retains/removes unreferenced old truth, re-plans, and runs validate + plan lint, reporting an honest bounded_status. Scope (no over-claim): v2.0.0 bounds the archive's file-count sprawl and removes unreferenced old truth while preserving referenced truth. It does not yet bound a single bundle's byte size — sharding is the next storage milestone. This is a major bump because of one breaking error-code-contract change (see Changed → MISSING_PHASE_FILE).

Changed

  • Behavior fix (error-code contract): doctor / validate now report a roadmap-referenced missing phase file as MISSING_PHASE_FILE, matching plan lint. Previously doctor (and validate, which delegates to it) emitted ORPHAN_PHASE_FILE (severity error) for a roadmap.yaml reference whose phase file is absent or present-but-inaccessible — the opposite of that code's documented meaning ("a phase file present but not referenced"), so a user looking the code up read a contradictory definition. The condition now uses the code whose name matches it (MISSING_PHASE_FILE, referenced but not present), with severity unchanged (error). ORPHAN_PHASE_FILE (warning) is unchanged and now means only present but unreferenced. Migration: a consumer that string-matched doctor / validate JSON for ORPHAN_PHASE_FILE to detect a missing referenced phase must switch to MISSING_PHASE_FILE; one that keys on severity (error vs warning) needs no change. plan lint already used MISSING_PHASE_FILE and is unaffected.
  • Docs: trimmed duplicated error-code tables from concept docs. concepts/finalization-reconciliation.md and concepts/governance.md now link to cli-contract.md § Error codes for exit codes / triggers / envelopes instead of restating them in their own tables (matching the existing concepts/runbook.md pattern). Reference detail stays in its single owner; the concept docs keep only the mental model. No code change.

Added

  • state archive-maintain [--keep-latest N] [--write] [--json] — the one
    high-level command that keeps the archive bounded.
    It orchestrates the
    existing archive primitives in the safe order (recover any pending
    delete-intent journal → compact-archive all kinds → archive-retention
    compact again if a follow-up materialised → re-plan → validateplan lint) so an operator no longer has to remember and order the low-level verbs.
    It adds no new destructive semantics and no new persistent state — a thin,
    honest orchestration over compactArchive / applyArchiveRetention and their
    journal recovery, writing nothing outside .code-pact/state/archive (no
    global maintenance ledger, no status/cache file, no timestamps/PIDs into
    tracked state — so it does not add a Git merge hotspot, and the same records
    fold to byte-identical bundles on independent branches). Dry-run by default
    (read-only, lock-free); --write runs the whole orchestration under one outer
    write lock. The result is reported honestly via a bounded_status: a
    source: both follow-up, a deferred mixed pair, an un-foldable record, or a
    pending journal all read as NOT bounded, and every skipped record is
    surfaced (never a silent drop). In healthy, compactable cases, compaction
    running first resolves ordinary mixed-source / source: both redundancy in the
    same maintenance run; deferred / skipped records (a bundle_stale divergence,
    an unsupported-platform fsync, a recovered bundle-pair survivor) remain
    explicitly not bounded and are reported with per-record reasons.
    Scope (no over-claim): v2.0.0 bounds the archive file-count sprawl and
    removes unreferenced old truth while preserving referenced truth. It
    does not yet bound a single bundle's byte size
    bundle_byte_size_bounded is always false; sharding is the next storage
    milestone. New public error code BUNDLE_PAIR_NOT_COMMITTABLE (a bundle-pair
    removal's pre-commit reverify found the store no longer matches the plan;
    fail-closed, re-plan and re-run). See
    cli-contract.md § state archive-maintain
    and docs/maintainers/operations.md § Archive maintenance.
  • plan sync-paths --rename <old>=<new> — apply an explicit old→new path
    rename to the reads / writes of every phase task. Renaming or merging a
    source file that a (often historical, done) phase still lists in its reads
    previously left plan lint --strict's reads-match invariant to be fixed by
    hand; this command does it deterministically. (A file that is gone for good is
    handled by removing the stale entry by hand — sync-paths only maps old→new.) Dry-run by default;
    --write applies under the write lock. Repeat --rename for multiple moves;
    entries that collapse to one path are de-duplicated. It only rewrites
    reads / writes of tasks under design/phases/ — never CHANGELOG or RFC
    prose. The TASK_READS_NO_MATCH lint message now names this command as the
    fix. See docs/troubleshooting.md.
  • pnpm gen:doc-blocks / check:doc-blocks — generate enumerable contract
    facts from code instead of hand-writing them.
    The first such block is the
    spec import data.detail table in cli-contract.md, now rendered from the
    typed SPEC_IMPORT_DETAILS catalog in src/contracts/spec-import-details.ts (a
    side-effect-free module the generator reads without pulling in command-handler
    deps; the duplicated enum list in spec-kit-bridge.md is replaced by a link).
    No CLI behavior change. The new check:doc-blocks (in check:docs) checks only
    generated-block drift — it never lints prose, style, or concept docs, so it
    can fail only a PR that touches the generated contract surface (the catalog, the
    generator, or the block itself); the fix is pnpm gen:doc-blocks. The decision,
    rollout, and the CI-burden contract every future doc check must satisfy are in
    the retired doc-truth-from-code-rfc.md (git history / .code-pact/state archive
    record) and the live design/rules/doc-authoring.md.
  • Generated detail enums for plan brief / plan constitution. The
    --from-file / --stdin data.detail enums now derive from a shared
    side-effect-free catalog (src/contracts/plan-capture-details.ts) consumed by
    both command runtimes, and a single cli-contract.md table is generated from it
    (both command sections link to it; drift-checked by check:doc-blocks). The
    generator gained |-escaping for table cells. No CLI behavior change.

Integrity

The published npm tarball (code-pact@2.0.0) — recorded per SECURITY.md supply-chain policy and verified by downloading the registry tarball and recomputing the digests:

  • shasum (sha1): 79b3ff58f980eb6ac284d0d1685c4a0ead526505
  • integrity (sha512): sha512-YXYr5GMpyE/gTuilY0Z595CmyTwQki6xvSmxrVfu6LTTWK6yASD7j5AJKd1a6nFNpQhLIGbwh7HlNaVY/4sLVg==

Verify:

curl -s https://registry.npmjs.org/code-pact/-/code-pact-2.0.0.tgz | shasum
# → 79b3ff58f980eb6ac284d0d1685c4a0ead526505

v1.32.0 — Collaboration UX: author attribution, status overview, attributed conflicts

05 Jun 12:51
v1.32.0
d0a7563

Choose a tag to compare

Collaboration: a team can now see who did what, who is on what, and where two
people collided
— without leaving git. Author attribution on every progress
event (D1), a read-only code-pact status team overview (D2), and
attribution-named progress-event conflicts (D3), plus the control-plane v2 PR1a
fail-closed phase-id resolver and the collaboration-safe-state gitignore /
id-conflict recovery follow-ups. All backward-compatible: valid existing
projects keep working, existing event ids are unchanged, and persisted changes
are additive/optional (the author progress-event field, the collaboration.author
config). Ambiguous or collaboration-broken states now surface earlier — via a
fail-closed error (AMBIGUOUS_PHASE_ID on duplicate phase ids) or an advisory
warning (CONTROL_PLANE_GITIGNORED) — instead of silently proceeding.

Added

  • Attributed PROGRESS_EVENT_CONFLICT + status conflicts[] (Collaboration
    UX RFC, D3). When two contributors record incompatible lifecycle events for one
    task (a done after done, a second started, an event after a terminal
    done — what two branches merging can produce), the existing
    PROGRESS_EVENT_CONFLICT warning now carries a structured details.events[]
    ({ event_id, status, author?, at }) naming the conflicting side(s) — the
    establishing event, when present, and the offender — so an agent reads who
    collided without parsing the
    message (author omitted per-event for legacy / capture-off events; event_id
    is the content id — the suffix of a per-event filename
    .code-pact/state/events/<at-compact>-<event_id>.yaml, not the whole name,
    while a legacy progress.yaml-only event has no per-event file). The same shape
    surfaces on every existing surface (plan analyze / doctor), and
    code-pact status now returns a
    data.conflicts[] array (PROGRESS_EVENT_CONFLICT only) carrying it —
    scoped to the selected tasks (narrowed by --phase), reported at scope level
    like totals and not narrowed by --mine (a conflict is a multi-author
    safety signal). Read-side only: no new gate, no exit change, no persisted state
    schema change (additive JSON output fields only); a healthy project gets
    conflicts: []. See
    design/decisions/collaboration-ux-rfc.md (D3) and docs/cli-contract.md
    § status / § Plan diagnostic codes.

  • code-pact status — team activity overview (Collaboration UX RFC, D2). A
    read-only top-level command (no --agent, no writes, no lock) that
    aggregates the derived state of every task and answers: what is in flight (by
    whom), what is blocked (why/by whom), what is free to pick up — and, for what
    isn't, why.
    data carries in_flight / blocked (with author + since
    from D1), available (planned + depends_on all done + any requires_decision
    satisfied by an accepted ADR — reusing the shared decision gate), waiting
    (with reasons[]: WAITING_FOR_DEPENDENCY / MISSING_DECISION), and totals.
    --mine filters active work to your resolved author identity, returning a data.filter
    that distinguishes "nothing is mine" from "can't tell who I am"
    (AUTHOR_CAPTURE_DISABLED / AUTHOR_UNAVAILABLE); --phase <id> scopes to one
    phase. Not a lock — it surfaces overlap so a team coordinates; it never
    reserves a task. Pure aggregation over deriveTaskState + depends_on + the
    decision resolver — no new core. (conflicts[] was added in D3 — see the entry
    above.) See design/decisions/collaboration-ux-rfc.md (D2) and
    docs/cli-contract.md § status.

  • Progress-event author attribution (Collaboration UX RFC, D1). Every
    progress event (task start / complete / block / resume / record-done)
    now records an optional author — the human who ran the verb — so a team's
    ledger answers who did what (it was otherwise actor-anonymous: actor +
    agent profile name only). Captured at write time by a fixed precedence:
    collaboration.author: off (project.yaml) wins first → omit; else
    CODE_PACT_AUTHOR (trimmed; blank-after-trim ignored); else git config user.name; else omit. No
    automatic user.email fallback
    (PII — set CODE_PACT_AUTHOR for
    email-as-identity). Additive and optional: legacy events omit it and hash
    identically to before
    (author joins the content id only when present, so two
    people recording the same transition produce distinct events surfaced by
    PROGRESS_EVENT_CONFLICT, never silently merged). New optional project.yaml
    collaboration: { author: auto | off }. Self-reported coordination metadata
    (as trustworthy as git blame), not an audit/security control. See
    design/decisions/collaboration-ux-rfc.md (D1) and docs/cli-contract.md
    § Author attribution. D2 (code-pact status overview) and D3 (attribution-named
    conflicts) ship together with D1 in this same release.

  • CONTROL_PLANE_GITIGNORED (collaboration-safe-state RFC A1 follow-up). An
    over-broad .gitignore rule silently defeats the whole collaboration model: any
    shared control-plane state — the per-event progress ledger, project.yaml,
    agent/model profiles, baselines — that does not reach git stays local, so a
    teammate or clean checkout misses whatever is ignored. Only when the ledger
    itself is ignored
    does the CONTROL_PLANE_BRANCH_NOT_DRIVEN CI gate also
    silently skip (no tracked ledger to read) — a config/profile/baseline-only
    ignore does not affect that gate.
    init merges its narrow ignore entries into an existing .gitignore
    and never deletes a user's lines, so a pre-existing rule survives and
    overrides them — the policy is written yet defeated. This adds two
    non-destructive detectors:

    • doctor reports CONTROL_PLANE_GITIGNORED (warning) authoritatively via
      git check-ignore --no-index over a representative file in each shared
      area (project.yaml, agent-profiles/, model-profiles/, state/baselines/,
      state/events/). Probing files, not directories, catches a file-scoped
      rule like state/events/*.yaml (the dir is not ignored, yet every new event
      file is); probing the whole control plane (not just the ledger) catches a
      config that re-includes only the ledger but still ignores project.yaml /
      profiles / baselines. Rule-based, so a force-added .gitkeep does not mask it
      and negation re-includes are honoured. The message names the affected
      area(s). Its structured recovery uses manual_action + confirm (the fix
      is a manual .gitignore edit, not a runnable command — so primary, which is
      contractually executable, is omitted). Silent skip when git is unavailable /
      not a repo, or .code-pact/project.yaml is absent. Advisory
      (severity: warning): doctor / default validate do not fail on it;
      validate --strict promotes it (like other doctor warnings), so CI can gate
      on it. Silence via .code-pact/doctor.yaml
      (disabled_checks: [CONTROL_PLANE_GITIGNORED]).
    • init now returns a warnings[] field (additive on InitResult) and warns
      when a pre-existing .gitignore would keep shared control-plane state off git.
      In a git repo it uses the same authoritative, whole-control-plane
      git check-ignore check as doctor (so a negation re-include is not a false
      positive, and a file-scoped rule is caught); before git init it falls back
      to a text heuristic for the blanket form and softens the wording to a
      possibility. Neither path edits the user's .gitignore. The check is shared
      via src/core/control-plane-ignore.ts so init and doctor cannot drift.
    • DoctorIssueRecovery gains optional manual_action and confirm fields
      (additive); primary is now optional. A manual-fix diagnostic sets
      manual_action (the edit instruction — never a shell command) + confirm (a
      runnable verify command) instead of primary, keeping primary strictly
      executable so an agent never runs prose. See
      design/decisions/collaboration-safe-state-rfc.md (A1) and the shared-vs-local
      table in docs/cli-contract.md § State file write guarantees.
  • Actionable recovery for collaboration id-conflict diagnostics (control-plane
    v2 PR1b, re-scoped). The conflict diagnostics that catch the dangerous
    clean-but-wrong branch merge — DUPLICATE_PHASE_ID, DUPLICATE_TASK_ID,
    PHASE_ID_MISMATCH — now carry a structured recovery object (the same shape
    as the CONTROL_PLANE_* advisories) wherever they surface (plan lint and
    doctor, in data.issues[]). doctor also now surfaces DUPLICATE_PHASE_ID
    (previously it detected only duplicate task ids), so the
    two-files-both-claim-P1 merge is caught on the doctor path too — parity with
    plan lint. The fix is a manual id rename, so recovery uses
    manual_action (rename one colliding id + update what references it) + confirm
    (code-pact plan lint) rather than a prose primary, keeping primary strictly
    executable; recovery.reference names what collides. New
    docs/troubleshooting.md § Id collisions & mismatches documents all five
    collaboration codes (the three above plus the fail-closed AMBIGUOUS_PHASE_ID /
    AMBIGUOUS_TASK_ID), and docs/agent-contract.md gains a Collaboration
    conflicts: fail closed, then recover
    section so an agent has a playbook for the
    exact failures the tool detects. No new diagnostics and no new default
    warnings
    — a valid current project (P<N> ids, inline tasks) stays as quiet
    under plan lint / doctor / --strict as before; this only enriches errors
    that already fire. Severity, codes, and exit behavior are unchanged.

    PR1b re-scope (note). The control-plane v2 RFC originally named
    warning-default LEGACY_SEQUENTIAL_PHASE_ID / LEGACY_INLINE_TASKS
    advisories for PR1b. Those are superseded / deferred: they would flag the
    current canonical lay...

Read more

v1.31.0 — collaboration-safe shared state

04 Jun 08:11
v1.31.0
2928d4c

Choose a tag to compare

Collaboration-safe shared state. The progress ledger moves from a single
.code-pact/state/progress.yaml array to one file per event under
.code-pact/state/events/, so contributors on separate branches can record
progress and merge cleanly — no lost updates, no merge-corrupted log. Existing
projects keep working unchanged: the legacy progress.yaml is read-merged and
never rewritten.

Scope note. This release makes the progress ledger collaboration-safe.
It does not change phase ids (P<N>), roadmap.yaml, or inline task
layout — the other multi-contributor control-plane conflicts are deferred to a
follow-up v2 control-plane RFC, gated on real demand (a second active
contributor or an external adopter).

Added

  • Per-event progress ledger (collaboration-safe-state RFC). task start /
    complete / block / resume / record-done write one content-addressed
    file under .code-pact/state/events/; readers merge it with the legacy
    progress.yaml deterministically (at, then content id). Concurrent writers
    and divergent branches no longer lose or reorder events — the write path is
    lock-free by construction (one no-overwrite file per event).
  • plan migrate — convert a legacy monolithic progress.yaml into the
    per-event ledger. Dry-run by default, idempotent, and it reports any task whose
    derived state would change under the merged ordering. The legacy file is left
    in place (never deleted or auto-rewritten).
  • PROGRESS_EVENT_CONFLICT diagnostic (doctor / plan analyze) — surfaces
    incompatible same-task lifecycle events (e.g. two branches that both done a
    task) instead of silently picking a winner. Advisory; validate --strict
    promotes it.
  • PLAN_MIGRATE_FAILED public error code — plan migrate's command-level
    failure (e.g. a corrupt event file), with the underlying cause in
    error.message.
  • EVENT_FILE_ID_MISMATCH ledger-integrity diagnostic (doctor /
    plan lint) — fail-closed when an event file's content does not match its
    content-addressed filename.

Changed

  • Shared-vs-local .code-pact/ policy is now consistent. init ignores only
    the machine-local / derived paths (locks/, cache/, plus /.local/,
    /.context/); the project config, baselines, and the progress ledger
    (state/events/**) are committed. The branch-drift gate
    (CONTROL_PLANE_BRANCH_NOT_DRIVEN) reads the committed ledger — legacy
    progress.yaml and state/events/**.
  • plan analyze / plan migrate wrap a ledger-read integrity failure into their
    own command-level code (PLAN_ANALYZE_FAILED / PLAN_MIGRATE_FAILED) rather
    than leaking EVENT_FILE_ID_MISMATCH as a top-level error.code. The
    governance / cli-contract docs are reconciled to the event-file safety model.

Fixed

  • init no longer ignores the progress ledger or commits machine-local lock
    files: it adds /.code-pact/locks/ and /.code-pact/cache/ to .gitignore
    (previously only /.local/ and /.context/ were ignored, so a user repo could
    commit pid/hostname lock state).

Integrity

Published npm tarball (code-pact@1.31.0), verified by downloading the registry tarball and recomputing both digests:

v1.30.1 — adapter-drift signal hygiene

03 Jun 09:04
v1.30.1
b21ec31

Choose a tag to compare

Adapter-drift signal hygiene. ADAPTER_GENERATOR_STALE no longer nags on a no-op patch bump — a stale generator_version stamp alone is silent when the generated adapter output is byte-identical to the manifest. The warning fires only when the desired output actually drifted. No new command, flag, diagnostic code, or manifest schema change.

Fixed

  • ADAPTER_GENERATOR_STALE no longer fires on version-stamp lag alone (#340). adapter doctor / global doctor previously raised this warning on a pure generator_version != packageVersion string inequality, so every version bump — including patch releases that change nothing about the generated adapter files — nagged users to run adapter upgrade. The check now fires only when the version differs and the current desired generated output is not byte-identical to the manifest (path set + per-file sha256, compared after the same dedupeDesiredFiles pass the install/upgrade engines use). A stamp-only lag is silent; when the agent profile can't be read (equivalence can't be proven), the warning is kept conservatively. ADAPTER_DESIRED_STALE / ADAPTER_FILE_DRIFT / ADAPTER_PROFILE_DRIFT and adapter upgrade --write write semantics are unchanged.

Full changelog: CHANGELOG.md · compare v1.30.0...v1.30.1

npm

npm install -g code-pact@1.30.1
Field Value
Version 1.30.1 (dist-tag latest)
Tarball https://registry.npmjs.org/code-pact/-/code-pact-1.30.1.tgz
shasum (sha1) 18bd53dcd5a44c59f19410afc995f9e433151247
integrity (sha512) sha512-3ROlOrj6AHiJbBsHx1NLxR8K4/BUAR4lRDCFz0Vt0X5jdWxL2cyTbE6UwtIbrGiNjwDXtLDD7luUiZYIFMUc+A==
sha512 (hex) dd13a53ab8fa0078896c1b07c7534bc51f0ae3f054011e25443085cf456dd17e63756c4bd9cc936c4e94c2d21bac688d8f00d7b4b0c3ee5b9489960814c51cf8

v1.30.0 — Context Fit

03 Jun 08:00
v1.30.0
12ee91a

Choose a tag to compare

Context Fit — make context pack size controllable, explainable, and lint-able, all additive and backward-compatible (P46–P50). Named budget profiles with --context-budget, a recommended budget on recommend / task prepare, byte-based explain metrics, and four opt-in plan lint --include-quality readiness advisories. The default no-flag context pack stays byte-identical; nothing here calls a model, tokenizer, or the network, and no budget is ever applied automatically.

Added

  • plan lint --include-quality gains four Context Fit advisories (P50, Context Fit layer d). A readiness layer that flags likely context-size risk before a task runs: TASK_CONTEXT_PACK_LARGE (the task's natural pre-elision context pack exceeds the balanced budget — 60000 bytes; details.natural_bytes / details.threshold_bytes / details.recommended_profile = "wide"), TASK_CONTEXT_BUDGET_UNACHIEVABLE (the deterministically recommended budget cannot fit even after maximal eligible elision — minimum_achievable_bytes > budget_bytes; details.profile / details.budget_bytes / details.minimum_achievable_bytes), TASK_DECLARED_DECISION_LARGE (a decision_refs body exceeds the tight budget — 30000 bytes; details.path / details.bytes / details.threshold_bytes), and TASK_READS_MATCH_TOO_MANY (a reads glob matches more than 100 files; details.glob / details.match_count / details.threshold_count). All four appear only under --include-quality, are absent without it, and are affects_exit: falseplan lint / plan lint --strict exit semantics are unchanged for advisory-only cases. Thresholds are deterministic byte/count values sourced from STANDARD_CONTEXT_BUDGET_PROFILES where applicable. The pass is local and deterministic: the pack-size advisories reuse the P49 explain metrics (natural_bytes and the same shared minimum_achievable_bytes floor CONTEXT_OVER_BUDGET uses, not a separate floor) via one cached context-pack build per task, the budget recommendation reuses the P48 mapping (honoring the default agent's same-name context_budget override when available, else the built-in fallback — the same byte value recommend / task prepare surface), and the decision-size / reads-count advisories read files / expand globs with per-run caches. No context pack content is changed and no budget is automatically applied, and no model, tokenizer, summarization, compression, semantic ranking, embeddings, or network is introduced. These are readiness signals, not correctness failures — a large pack, a large declared decision, or a broad reads glob can all be legitimate. P50 adds no new flag or command (the advisories surface through the existing --include-quality flag).
  • task context --explain --json gains additive Context Fit byte metrics (P49, Context Fit layer c). Alongside the unchanged total_bytes / context_pack_bytes / sections / excluded, an --explain --json build now reports natural_bytes (the pre-budget pack size), final_bytes (equal to total_bytes == context_pack_bytes), saved_bytes (natural_bytes - final_bytes), saved_ratio (saved_bytes / natural_bytes), minimum_achievable_bytes (the floor after all budget-eligible elisions for this task), and elided_sections (the budget-elided sections only, in elision order). budget_bytes is present only when a budget was applied (via --budget-bytes or --context-budget) and reflects the resolved value (including an agent same-name override). With no budget, natural_bytes === final_bytes, saved_bytes === 0, saved_ratio === 0, and elided_sections === []. minimum_achievable_bytes is the same floor CONTEXT_OVER_BUDGET reports, computed by one shared helper — the success path and the error path can never disagree — honoring the P28 conditional eligibility (related_decisions elidable only when context_size: large; rules only when write_surface: high). The metrics are byte-based, not token-based, computed locally and deterministically; they make the existing P24/P47 budget behavior observable and introduce no new context reduction — the no-flag pack stays byte-identical, only an explicit budget can elide, and metrics surface only on --explain --json (not on normal task context output or on task prepare). No tokenizer, summarization, compression, semantic ranking, embeddings, or network behavior is introduced.
  • recommend / task prepare gain an optional contextFit recommendation (P48, Context Fit layer b). RecommendResultV2 now carries an optional, strictly-additive contextFit — a recommended standard context budget profile (recommendedProfile: tight / balanced / wide), its recommendedBudgetBytes, and a one-line reason. The profile is derived deterministically from existing task readiness fields: context_size == large OR ambiguity == high OR write_surface == highwide; else context_size == mediumbalanced; else tight. requires_decision does not shrink it. recommendedBudgetBytes is an agent profile's same-named context_budget override when present, else the P47 built-in fallback (tight 30000, balanced 60000, wide 120000); recommendedProfile is a closed enum and never emits a custom agent-profile name. It surfaces in recommend --json, a one-line recommend human-output entry, and task prepare --json (under recommendation.contextFit). It is a suggestion: it is not auto-applied — re-sizing the pack stays explicit via --context-budget <profile>, the default no-flag pack stays byte-identical, and the task prepare commands dictionary still does not echo --context-budget. contextFit is distinct from the categorical budgetProfile (no overload). No token counting, tokenizer, summarization, compression, semantic ranking, embeddings, or network behavior is introduced.
  • task context / task prepare gain --context-budget <profile> (P47, Context Fit layer a). A named, ergonomic alias for a byte budget: the profile resolves to a max_bytes value that then drives the unchanged P24 --budget-bytes enforcement path (same locked elision order, same CONTEXT_OVER_BUDGET). Three built-in profiles ship — tight (30000), balanced (60000), wide (120000) — and resolve even with no agent profile selected. An agent profile may declare an optional context_budget block to override a built-in byte value or name custom profiles; default_profile is validated but not auto-applied. --context-budget and --budget-bytes are mutually exclusive (CONFIG_ERROR, exit 2); an unknown profile is CONFIG_ERROR. The no-flag default pack stays byte-identical, --budget-bytes is unchanged, and the task prepare commands dictionary does not echo --context-budget. wide is intentionally not full — it can still elide or hit CONTEXT_OVER_BUDGET. No tokenizer, summarization, or network behavior is introduced.

Full changelog: https://github.com/toshtag/code-pact/blob/v1.30.0/CHANGELOG.md

Install

npm install -g code-pact@1.30.0

Package (npm registry)

field value
version 1.30.0 (dist-tag: latest)
tarball https://registry.npmjs.org/code-pact/-/code-pact-1.30.0.tgz
shasum (sha1) a90b0bf4b160047a3094e5b3a6c11834b69cf18e
integrity (sha512) sha512-EL70Fyxb9vFS/o90wlaqNu/+jPAXJvV0sqi/OnyU9LXbjzRbGT8esz8SKs8Rx2C/q2kGQwbOgsg5VTSDp8Ax6A==
integrity (sha512 hex) 10bef4172c5bf6f152fe8f74c256aa36effe8cf01726f574b2a8bf3a7c94f4b5db8f345b193f1eb33f122acf11c760bfab69064306ce82c839553483a7c031e8

v1.29.2 — adapter upgrade remaining-advisory hint

02 Jun 13:54
v1.29.2
c6087f3

Choose a tag to compare

Adapter-upgrade friction cut. adapter upgrade --write now explains, in place, why a MODEL_MAP_STALE advisory can survive a write — closing the "I upgraded, why is one advisory still there?" gap without growing the command's job. No new command, flag, schema field, or error code; the change is human-output and an internal refactor.

Added

  • adapter upgrade claude-code --write surfaces a remaining-advisory hint. adapter upgrade repairs generator/desired file drift but deliberately never rewrites a profile's model_map (a pin may be intentional), so a MODEL_MAP_STALE advisory persists across a --write. A successful --write with no refused files that leaves the claude-code model_map pinned to a known-but-not-current catalog id now prints a human-only Remaining manual advisory: MODEL_MAP_STALE note on stderr — naming the stale tier, the current default, the profile path to hand-edit, and the .code-pact/doctor.yaml silence path. It never advises --model (which re-pins model_version, not model_map) and never mutates model_map. Scoped to claude-code's model_map only; it does not run the global doctor or widen to other advisories. It honors the same suppression as doctor (a project with disabled_checks: [MODEL_MAP_STALE] gets no hint) and is withheld when any file was refused (there the actionable step is --accept-modified). The --json envelope is unchanged (doctor --json remains the machine-readable source); the hint is human-output only.

Changed

  • The MODEL_MAP_STALE condition is now a single shared function (src/core/models/model-map-drift.ts). doctor and the new adapter upgrade hint both derive staleness from detectModelMapDrift, so the two can never disagree about whether a profile is stale. Pure + offline. No behavior change to doctor's existing MODEL_MAP_STALE / MODEL_ID_UNKNOWN output.
  • doctor.yaml loading is a single shared module (src/core/doctor-config.ts). The DoctorConfig schema + loader moved out of doctor.ts so doctor and the adapter upgrade hint read disabled_checks identically — one suppression source.

Full changelog: https://github.com/toshtag/code-pact/blob/v1.29.2/CHANGELOG.md

Install

npm install -g code-pact@1.29.2

Package (npm registry)

field value
version 1.29.2 (dist-tag: latest)
tarball https://registry.npmjs.org/code-pact/-/code-pact-1.29.2.tgz
shasum (sha1) 587b3f7a81b31ef0ec93c1f1eaac7a1f76b69d04
integrity (sha512) sha512-Rrbh0dIKEaR3LSOmZzoCtKcJ6WSaHuY18Zc6V/PLWmfFQweXI5j22lNKMLT2oI/4zGNxB3Wy9x3a2DhgHH7zXw==
integrity (sha512 hex) 46b6e1d1d20a11a4772d23a6673a02b4a709e9649a1ee635f1973a57f3cb5a67c54307972398f6da534a30b4f6a08ff8cc63710775b2f71ddad838601c7ef35f

v1.29.1 — Context-pack write contract hygiene (P45)

02 Jun 12:24
v1.29.1
ef92846

Choose a tag to compare

Contract hygiene. Context-pack writes are now atomic (matching the table-scoped atomicWriteText guarantee for managed file-content writes), the docs no longer blur which command writes the pack, and the fresh-project doctor / agent-profile-path fixes from this cycle ship in the same patch. No new surface — all changes are advisory/internal or correct an existing contract.

Changed

  • Agent-profile path resolution is unified across commands. adapter install / adapter upgrade / adapter doctor / adapter list, recommend, task prepare, pack, and the model_version pin all now honor project.yaml's agents[].profile (the path doctor already used), via a single resolveAgentProfilePath helper. The conventional agent-profiles/<name>.yaml remains the fallback when project.yaml is absent or the agent is unlisted; a matched agent whose profile is an invalid path now fails with CONFIG_ERROR rather than silently using the default. Default projects are unaffected. Completes the follow-up noted in 1.29.0 (#330).

Fixed

  • Context-pack writes are now atomic, matching the published contract (closes P45). writeContextPack() (used by task prepare and the low-level pack) wrote the pack with a raw writeFile, so an interrupted process could leave a half-written .context/<agent>/<task-id>.md — exactly what cli-contract.md § "State file write guarantees" says cannot happen for the managed file-content writes it lists. It now uses atomicWriteText (temp-file + rename); the redundant explicit mkdir is dropped. Output path and pack bytes are unchanged. The write-guarantees table gains an explicit context-pack row, and the <adapter-owned files> row no longer claims the adapter writes the per-task packs. positioning.md, glossary.md, and agent-contract.md distinguish task context (read-only — builds/returns the pack) from task prepare / pack (the writers); a narrow check:doc-invariants rule guards both the false-writer claim and the contract↔code atomic-write pairing. No new command, flag, schema field, or error code.
  • doctor no longer nags for design/brief.md on a fresh project. BRIEF_MISSING is now gated on a real (non-TUTORIAL) phase existing, matching the CONSTITUTION_PLACEHOLDER gate. A fresh non-interactive init surfaces ADAPTER_MISSING + ADAPTER_STALE, not BRIEF_MISSING. getting-started.md gained a "What init creates" table.

Maintenance (no runtime/package effect)

  • Moved maintainer release evidence from design/measurements/ to docs/maintainers/measurements/, separating the active control plane (design/) from generated artifacts (the npm package ships neither).

Integrity

Published npm tarball (code-pact@1.29.1):

shasum:    e11e9f573400503013b9876fab12bd3c1558c921
integrity: sha512-8lgHXr692+H98SpOOd9eBWPETD0MbB2YiXRFOX39S2aUBEYfVdeeg95fuHmMWJl36nbZvdsWLfgmS6J6gX4msg==

Git tag v1.29.1 is SSH-signed (GitHub: Verified).

v1.29.0 — model-config accuracy

02 Jun 05:50
v1.29.0
aebd794

Choose a tag to compare

model-config accuracy. Claude model facts are now a single source of truth, Opus 4.8 is supported, model drift is detected offline, and the codex defaults are current. No breaking changes — the surface is advisory (recommendation display, generated guidance, doctor diagnostics) and the new doctor codes are additive.

Added

  • Single-source model catalog (src/core/models/catalog.ts) — version list, --model aliases, vendor model ids, default model_map, model profiles, and guidance text in one leaf module. Bumping a model is a one-file edit.
  • Claude Opus 4.8 support--model opus-4.8 / claude-opus-4-8 accepted; default highest_reasoning for the claude-code adapter is now claude-opus-4-8. Fixes the help example that used to fail CONFIG_ERROR.
  • Offline model-drift doctor checks (claude-code): MODEL_ID_UNKNOWN and MODEL_MAP_STALE — warnings, offline, scoped so codex/other agents never false-positive, suppressible via .code-pact/doctor.yaml.

Changed

  • codex model_map refreshed to the current OpenAI Codex lineup (gpt-5.5 / gpt-5.4 / gpt-5.4-mini). Advisory display only; codex/gemini model_map is user-maintained (model governance stays claude-code-only).
  • Generation-resistant CLAUDE.md guidance — one thinking/effort note accurate for every current Claude version (adaptive thinking + effort), replacing per-model prose that drifted. Tier label is now (thinking-capable).

Fixed

  • A vendor-id model_version (e.g. claude-opus-4-8) renders the correct guidance instead of the generic fallback.
  • MODEL_MAP_STALE remediation names the profile path doctor evaluated.
  • Doc accuracy: MISSING_MODEL_TIER is a warning; the adapter install --model description reflects adaptive thinking, Opus 4.8, vendor-id aliases, and CONFIG_ERROR on unknown.

Install

npm install -g code-pact@1.29.0

Distribution

  • npm: code-pact@1.29.0 (dist-tag latest)
  • tarball: https://registry.npmjs.org/code-pact/-/code-pact-1.29.0.tgz
  • shasum (sha1): aa8db647751a7267cf6c777533ebdb3498e60ce7
  • integrity (sha512): sha512-HG6Ty2Vaqe6rFvTjlGE0dlAJvMzF3Eddap4COjhkm9WnShL8fCii3EFUSvCEo0EaYloJf1O381uF5Q7k6evj3A==
  • files: 5 · unpacked: ~1.98 MB

Known follow-up: unifying agent-profile path resolution across all commands for non-default agents[].profile#330.

v1.28.0 — maintenance surface + agent discoverability

02 Jun 01:26
v1.28.0
185dba7

Choose a tag to compare

Maintenance-surface reduction and agent discoverability, driven by external agent feedback on 1.27.0. No behavior change to task command parsing.

Maintenance surface reduction

Changed

  • docs/ja mirror removed. The Japanese docs were a full mirror of the first-run / user guides, hand-synced every release. docs/ja/ now keeps only README.md as an entry point into the English docs (the single source). CLI / adapter runtime output stays localizable; the spec/contract/usage docs do not. The per-release JA sync obligation is gone.
  • task CLI reference is generated from a single source. A CommandSpec per task subcommand (src/cli/spec/) is now the one place the flag surface is declared; parse (toParseOptions), help (renderLeafHelp), and docs/cli-reference.generated.md (renderReference) all derive from it. All 11 task verbs ported — the task-specific hand-written help entries were removed from src/cli/usage.ts. Task flag-surface drift across parse/help/reference is structurally impossible. Non-task rich help remains hand-written for now. Design in design/decisions/cli-command-spec-rfc.md.
  • cli-contract.md task flag-table duplication removed. The 11 task flag tables / usage / examples in docs/cli-contract.md became pointers to the generated reference; the JSON envelopes, exit/error codes, enums, and other semantics stay (they are not generatable from the spec).

Agent discoverability

Added

  • Machine-readable recovery on CONTROL_PLANE_* issues. CONTROL_PLANE_NOT_DRIVEN and CONTROL_PLANE_BRANCH_NOT_DRIVEN carry an additive recovery object ({ primary, alternatives?, reference? }) alongside the unchanged message, so an agent can pick the next action from JSON without parsing prose. It rides through validate --json (both stringify the same DoctorResult). Scoped to the two CONTROL_PLANE_* codes; documented under docs/cli-contract.md § Doctor diagnostic codes.
  • Richer leaf help for mutating commands. The 9 mutating non-task commands (plan brief / adopt / constitution / normalize, phase add / new / reconcile, adapter install / upgrade) now have rich --help with synopsis, options where applicable, and examples, replacing the 2-line stub.

Guardrails

Added

  • Generated CLI reference check. pnpm check:cli-reference (wired into check:docs and CI) fails if docs/cli-reference.generated.md is stale relative to the specs.
  • Leaf help coverage test. Pins that every mutating / JSON-emitting plan / phase / adapter command answers --help with rich help (or is an explicit, shrinking allowlisted stub), so a new such command can't ship as a silent stub.
  • Agent feedback corpus. design/measurements/agent-feedback/ records external-agent feedback, classified (implemented-but-undiscovered / docs-duplication / genuinely-missing), so prioritization doesn't depend on a chat log.

Explicit non-changes

  • No docs/ownership.yaml. A path-diff "you touched src but not docs" checker is a heuristic, not a derivable invariant — it would add a false-positive surface and a new managed file, against the goal of this arc. Revisit only as a specific check-doc-invariants rule, if one becomes derivable.
  • No standalone docs-reduction or record_only-dedupe pass. The real duplication was already absorbed by the docs/ja removal and the cli-contract cut; what remained on inspection was primary sources, already-linked pointers, and context-bound mentions — not removable duplication.
  • No behavior change to task command parsing. The verified flag sets are preserved; only the source of the flag surface (and where help/reference come from) changed.

Integrity

The published npm tarball for code-pact@1.28.0:

Verified by downloading the published tarball and recomputing shasum (matches the registry value above).

v1.26.0 — Reshape roadmap P32–P37 + identifier/path hardening

30 May 06:59
v1.26.0
0578915

Choose a tag to compare

Identifier & path hardening (security)

Plan identifiers and agent-profile path fields flow into agent-facing command strings and filesystem paths. They are now constrained at the schema boundary so untrusted or malformed plan content cannot produce broken commands or escape the project root.

Security

  • Plan-identifier charset. Task.id, Phase.id, roadmap PhaseRef.id, and agent names (Project.default_agent, AgentRef.name, AgentProfile.name) are constrained to ^[A-Za-z0-9][A-Za-z0-9._-]*$ — the leading character must be alphanumeric, which rejects .., slashes, whitespace, shell metacharacters, and option-like ids (--json, -P1) that a generated command would otherwise misread as a flag. Enforced on the write entrypoints too (phase import, phase add / createPhase, task add --id) and on the raw --agent of recommend / pack; the write paths run Phase.parse / PhaseRef.parse before persisting. Conventional ids (P1-T1, P34-ci-branch-drift, claude-code) are unaffected.
  • Agent-profile path fields. AgentProfile.instruction_filename / context_dir / skill_dir / hook_dir and AgentRef.profile are now project-relative POSIX paths (RelativePosixPath): absolute paths, ~, .., ., empty segments, and backslashes are rejected, so a profile cannot redirect context-pack or adapter writes outside the project root.
  • Project-root confinement in pack. Context-pack reads and writes are confined via resolveWithinProject (lexical traversal and symlink escape): decision_refs, the design/decisions / design/rules readdir loaders, and the .context/<agent> output directory. The acceptance_refs existence checks and the --baseline name are likewise constrained.

Fixed

  • CONTEXT_OVER_BUDGET envelope. task context / task prepare --budget-bytes now place budget_bytes / minimum_achievable_bytes / unelidable_sections under a top-level data (matching the documented envelope convention and doctor / validate), not under error.data. An agent following the cli-contract recovery prose (data.minimum_achievable_bytes) now finds the fields.

Roadmap: P37 (outcome audit) deferred

Internal

  • P37 deferred — the reshape roadmap's outcome-audit / effectiveness-measurement item is deferred with no implementation: no phase, no stats / task outcome command, no outcome schema field. Subjective agent-reported metrics are gameable, a bugs-found metric contradicts code-pact's control-plane role, and the signals worth measuring (recommendation history, decision-gate blocks, verify failures) are not reliably derivable from progress.yaml today. The reshape closes at P36; the next step is real-world dogfooding and release prep. Rationale + revisit conditions in design/decisions/P37-deferred-outcome-audit.md.

ADR quality advisory (P36)

The value of the ADR gate is that writing the ADR elicits the decision — but today an ADR only needs status: accepted to pass, so an accepted ADR with an empty body slips through. This surfaces that, without enforcing a template. Design in design/decisions/adr-quality-advisory-rfc.md.

Added

  • ADR_ACCEPTED_BODY_THIN — a single plan lint --include-quality advisory (warning, affects_exit: false) that flags an accepted ADR whose body is an empty stub. Structure-independent — no heading-name matching: fires only when the substantive body (frontmatter removed, status line + h1 title stripped, whitespace normalized) is below ADR_THIN_BODY_CHARS (400) AND the raw body has zero ## (h2) headings. This repo's legitimate ADRs vary widely in structure (they never use ## Consequences / ## Alternatives), so name-matching would false-positive; the AND keeps "short but structured" and "long but heading-free" ADRs from firing. A file that is just a **Status:** accepted line is in scope; a 0-byte empty file and proposed/draft ADRs are not. Advisory only — the task complete / verify decision gate is unchanged. (A canonical-template-based check, if ever wanted, is a separate future phase.)

CI branch-drift detection (P34)

The working-tree CONTROL_PLANE_NOT_DRIVEN (v1.25) never fires in PR CI because the checkout is clean. This adds a branch-diff signal so CI can catch "real code changed but the control plane was not driven on this branch." Design in design/decisions/ci-branch-drift-rfc.md.

Added

  • --base-ref <ref> on doctor / validate and a new advisory CONTROL_PLANE_BRANCH_NOT_DRIVEN. The check runs only when --base-ref is supplied and compares the branch diff (merge-base..HEAD, via the existing auditWrites merge-base mode). It fires when real, non-excluded files changed but the branch added no event that is started/done AND non-TUTORIAL AND a task_id present in the loaded plan — so merely touching progress.yaml (or appending an unrelated/unknown/TUTORIAL event) does not pass. A started or done for a known task suppresses it (usage detection, not completion guarantee).
  • Gate model. Advisory (severity: warning) by default — plain doctor / validate exit unchanged. validate --strict --base-ref origin/main promotes it to exit 1 (existing strict semantics). Three-layer opt-in: no --base-ref → does not run; no --strict → does not fail; disabled_checks → off.
  • Team-declared exemptions. .code-pact/doctor.yaml control_plane_branch_not_driven.exclude_globs (default empty — no built-in docs/config exemption) lets a repo declare paths whose change does not require driving the loop.
  • Conservative skips. Silent skip when git/merge-base is unavailable, progress.yaml is not git-tracked (the ledger must be committed for CI to audit it), or HEAD progress.yaml is unparseable (INVALID_YAML/SCHEMA_ERROR owns that). docs ship a version-pinned GitHub Actions example; no ci scaffold command.

Failure clarity for task complete / task finalize (P32)

When a task complete / task finalize failure occurred, the root cause already existed in the result but the surfaces an agent reads hid it — human output was a single generic line and JSON only carried data.verify.checks. An agent had to re-run the lower-level verify to decide its next action. This surfaces the cause and the next action at the point of failure. Design in design/decisions/failure-clarity-rfc.md.

Added

  • Three additive data fields on the task complete / task finalize failure envelopesfailed_checks: string[], first_failure: { name, reason } | null, and suggested_next_command: string | null, placed alongside the unchanged data.verify.checks / data.write_audit. suggested_next_command is a deterministic, AI-free switch on the failing check (or finalize code) → an exact command, and is a rerun-after-fixing command (it does not imply an unchanged rerun will succeed). task complete --dry-run failures carry the same fields (verification runs before the dry-run short-circuit). task finalize synthesizes a pseudo-check per failure code (eligibility / write_safety / write_audit). No new error codes, and existing fields are unchanged, so any consumer that ignores unknown fields is unaffected.
  • Richer human output — below the existing generic failure message, cause: and a rerun after fixing: line (ja: 原因: / 修正後に再実行:) are printed to stderr. The label is deliberately not next:, so an agent does not read it as "just rerun unchanged".

Internal

  • New pure helper src/core/failure/failure-summary.ts (no dependency on src/commands/; takes a structural FailureCheckLike) and a shared CLI renderer src/cli/render/failure-summary.ts, reused by both task complete and task finalize.

Lightweight lane + recommendation consumption (P33)

recommend / task prepare returned a correct execution profile, but every task ran the full loop (ceremony for small, strongly-verified work) and nothing told an agent to consume the recommendation — a correct recommendation was produced but not acted on. This adds a lifecycle signal and the consumption contract together. Design in design/decisions/lightweight-lane-rfc.md.

Added

  • lifecycleMode on recommend / task prepare — an additive field (full_loop | record_only | decision_loop) from a conservative deterministic switch: decision_loop when the task or phase requires_decision; record_only when type ∈ {docs, test} AND ambiguity == low AND risk == low AND verification_strength == strong; otherwise full_loop. architecture is not auto-decision_loop. Advisory only — code-pact's own task complete / task record-done behavior is unchanged.
  • Recommendation-consumption guidance in generated adapters (both locales) — the "What to verify first" axis now tells agents to read data.recommendation as an execution profile (tier/effort/planningRequired/lifecycleMode), to report a limitation when the runtime cannot switch model, and that record_only is a lighter loopnot lighter verification (run the project verification commands, then task record-done --evidence).
  • Three version-gated adapter conformance checksrecommendation_consumption_guidance_present, lifecycle_mode_guidance_present, cannot_switch_model_fallback_present verify the guidance is present (anchored on short stable tokens). Gated on a new RECOMMENDATION_CONSUMPTION_FROM_VERSION threshold (not the P30 one), so adapters generated between the P30 and P33 releases stay advisory rather than failing en masse.

Internal

  • New src/core/recommend/lifecycle.ts (recommendLifecycleMode, reuses isDecisionRequiredForTask); resolveRecommendation takes a meaning-closed decisionContext. No agent_action JSON field (it would duplicate the prose guidance without adding enforcement).

Integrity

Published npm tarball (code-pact@1.26.0), value...

Read more