Releases: toshtag/code-pact
v2.0.0 — bounded archive maintenance
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/validatenow report a roadmap-referenced missing phase file asMISSING_PHASE_FILE, matchingplan lint. Previouslydoctor(andvalidate, which delegates to it) emittedORPHAN_PHASE_FILE(severityerror) for aroadmap.yamlreference 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-matcheddoctor/validateJSON forORPHAN_PHASE_FILEto detect a missing referenced phase must switch toMISSING_PHASE_FILE; one that keys onseverity(error vs warning) needs no change.plan lintalready usedMISSING_PHASE_FILEand is unaffected. - Docs: trimmed duplicated error-code tables from concept docs.
concepts/finalization-reconciliation.mdandconcepts/governance.mdnow link tocli-contract.md§ Error codes for exit codes / triggers / envelopes instead of restating them in their own tables (matching the existingconcepts/runbook.mdpattern). 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-archiveall kinds →archive-retention→
compact again if a follow-up materialised → re-plan →validate→plan 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 overcompactArchive/applyArchiveRetentionand 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);--writeruns the whole orchestration under one outer
write lock. The result is reported honestly via abounded_status: a
source: bothfollow-up, a deferred mixed pair, an un-foldable record, or a
pending journal all read as NOT bounded, and everyskippedrecord is
surfaced (never a silent drop). In healthy, compactable cases, compaction
running first resolves ordinary mixed-source /source: bothredundancy in the
same maintenance run; deferred / skipped records (abundle_staledivergence,
an unsupported-platformfsync, 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_boundedis alwaysfalse; sharding is the next storage
milestone. New public error codeBUNDLE_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
anddocs/maintainers/operations.md§ Archive maintenance.plan sync-paths --rename <old>=<new>— apply an explicit old→new path
rename to thereads/writesof every phase task. Renaming or merging a
source file that a (often historical, done) phase still lists in itsreads
previously leftplan 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;
--writeapplies under the write lock. Repeat--renamefor multiple moves;
entries that collapse to one path are de-duplicated. It only rewrites
reads/writesof tasks underdesign/phases/— never CHANGELOG or RFC
prose. TheTASK_READS_NO_MATCHlint 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 importdata.detailtable incli-contract.md, now rendered from the
typedSPEC_IMPORT_DETAILScatalog insrc/contracts/spec-import-details.ts(a
side-effect-free module the generator reads without pulling in command-handler
deps; the duplicated enum list inspec-kit-bridge.mdis replaced by a link).
No CLI behavior change. The newcheck:doc-blocks(incheck: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 ispnpm gen:doc-blocks. The decision,
rollout, and the CI-burden contract every future doc check must satisfy are in
the retireddoc-truth-from-code-rfc.md(git history /.code-pact/statearchive
record) and the live design/rules/doc-authoring.md.- Generated detail enums for
plan brief/plan constitution. The
--from-file/--stdindata.detailenums now derive from a shared
side-effect-free catalog (src/contracts/plan-capture-details.ts) consumed by
both command runtimes, and a singlecli-contract.mdtable is generated from it
(both command sections link to it; drift-checked bycheck: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
# → 79b3ff58f980eb6ac284d0d1685c4a0ead526505v1.32.0 — Collaboration UX: author attribution, status overview, attributed conflicts
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+statusconflicts[](Collaboration
UX RFC, D3). When two contributors record incompatible lifecycle events for one
task (adoneafterdone, a secondstarted, an event after a terminal
done— what two branches merging can produce), the existing
PROGRESS_EVENT_CONFLICTwarning now carries a structureddetails.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 (authoromitted 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 legacyprogress.yaml-only event has no per-event file). The same shape
surfaces on every existing surface (plan analyze/doctor), and
code-pact statusnow returns a
data.conflicts[]array (PROGRESS_EVENT_CONFLICTonly) carrying it —
scoped to the selected tasks (narrowed by--phase), reported at scope level
liketotalsand 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) anddocs/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.datacarriesin_flight/blocked(withauthor+since
from D1),available(planned +depends_onall done + anyrequires_decision
satisfied by an accepted ADR — reusing the shared decision gate),waiting
(withreasons[]:WAITING_FOR_DEPENDENCY/MISSING_DECISION), andtotals.
--minefilters active work to your resolved author identity, returning adata.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 overderiveTaskState+depends_on+ the
decision resolver — no new core. (conflicts[]was added in D3 — see the entry
above.) Seedesign/decisions/collaboration-ux-rfc.md(D2) and
docs/cli-contract.md§status. -
Progress-event
authorattribution (Collaboration UX RFC, D1). Every
progress event (task start/complete/block/resume/record-done)
now records an optionalauthor— 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); elsegit config user.name; else omit. No
automaticuser.emailfallback (PII — setCODE_PACT_AUTHORfor
email-as-identity). Additive and optional: legacy events omit it and hash
identically to before (authorjoins 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 optionalproject.yaml
collaboration: { author: auto | off }. Self-reported coordination metadata
(as trustworthy asgit blame), not an audit/security control. See
design/decisions/collaboration-ux-rfc.md(D1) anddocs/cli-contract.md
§ Author attribution. D2 (code-pact statusoverview) 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.gitignorerule 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 theCONTROL_PLANE_BRANCH_NOT_DRIVENCI gate also
silently skip (no tracked ledger to read) — a config/profile/baseline-only
ignore does not affect that gate.
initmerges 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:doctorreportsCONTROL_PLANE_GITIGNORED(warning) authoritatively via
git check-ignore --no-indexover 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 likestate/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 ignoresproject.yaml/
profiles / baselines. Rule-based, so a force-added.gitkeepdoes not mask it
and negation re-includes are honoured. Themessagenames the affected
area(s). Its structuredrecoveryusesmanual_action+confirm(the fix
is a manual.gitignoreedit, not a runnable command — soprimary, which is
contractually executable, is omitted). Silent skip when git is unavailable /
not a repo, or.code-pact/project.yamlis absent. Advisory
(severity: warning):doctor/ defaultvalidatedo not fail on it;
validate --strictpromotes it (like other doctor warnings), so CI can gate
on it. Silence via.code-pact/doctor.yaml
(disabled_checks: [CONTROL_PLANE_GITIGNORED]).initnow returns awarnings[]field (additive onInitResult) and warns
when a pre-existing.gitignorewould keep shared control-plane state off git.
In a git repo it uses the same authoritative, whole-control-plane
git check-ignorecheck asdoctor(so a negation re-include is not a false
positive, and a file-scoped rule is caught); beforegit initit 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
viasrc/core/control-plane-ignore.tssoinitanddoctorcannot drift.DoctorIssueRecoverygains optionalmanual_actionandconfirmfields
(additive);primaryis now optional. A manual-fix diagnostic sets
manual_action(the edit instruction — never a shell command) +confirm(a
runnable verify command) instead ofprimary, keepingprimarystrictly
executable so an agent never runs prose. See
design/decisions/collaboration-safe-state-rfc.md(A1) and the shared-vs-local
table indocs/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 structuredrecoveryobject (the same shape
as theCONTROL_PLANE_*advisories) wherever they surface (plan lintand
doctor, indata.issues[]).doctoralso now surfacesDUPLICATE_PHASE_ID
(previously it detected only duplicate task ids), so the
two-files-both-claim-P1merge is caught on thedoctorpath too — parity with
plan lint. The fix is a manual id rename, sorecoveryuses
manual_action(rename one colliding id + update what references it) +confirm
(code-pact plan lint) rather than a proseprimary, keepingprimarystrictly
executable;recovery.referencenames what collides. New
docs/troubleshooting.md§ Id collisions & mismatches documents all five
collaboration codes (the three above plus the fail-closedAMBIGUOUS_PHASE_ID/
AMBIGUOUS_TASK_ID), anddocs/agent-contract.mdgains 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
underplan lint/doctor/--strictas 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-defaultLEGACY_SEQUENTIAL_PHASE_ID/LEGACY_INLINE_TASKS
advisories for PR1b. Those are superseded / deferred: they would flag the
current canonical lay...
v1.31.0 — collaboration-safe shared state
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-donewrite one content-addressed
file under.code-pact/state/events/; readers merge it with the legacy
progress.yamldeterministically (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 monolithicprogress.yamlinto 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_CONFLICTdiagnostic (doctor/plan analyze) — surfaces
incompatible same-task lifecycle events (e.g. two branches that bothdonea
task) instead of silently picking a winner. Advisory;validate --strict
promotes it.PLAN_MIGRATE_FAILEDpublic error code —plan migrate's command-level
failure (e.g. a corrupt event file), with the underlying cause in
error.message.EVENT_FILE_ID_MISMATCHledger-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.initignores 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.yamlandstate/events/**. plan analyze/plan migratewrap a ledger-read integrity failure into their
own command-level code (PLAN_ANALYZE_FAILED/PLAN_MIGRATE_FAILED) rather
than leakingEVENT_FILE_ID_MISMATCHas a top-levelerror.code. The
governance / cli-contract docs are reconciled to the event-file safety model.
Fixed
initno 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:
- tarball: https://registry.npmjs.org/code-pact/-/code-pact-1.31.0.tgz
- shasum (SHA-1):
8326b5e598c47139933bf5e27a04e5a5a30f74af - integrity (SHA-512):
sha512-wGOH/pz1NUcntxmrEKjZLhY/yS+j/+PXfJ9u1EzhkEuFYVAKZU3DqSG/wMMulY6Lyh5HoAiegifUf6Up9SyBpA==
v1.30.1 — adapter-drift signal hygiene
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_STALEno longer fires on version-stamp lag alone (#340).adapter doctor/ globaldoctorpreviously raised this warning on a puregenerator_version != packageVersionstring inequality, so every version bump — including patch releases that change nothing about the generated adapter files — nagged users to runadapter 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 samededupeDesiredFilespass 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_DRIFTandadapter upgrade --writewrite 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
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-qualitygains 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 thebalancedbudget — 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(adecision_refsbody exceeds thetightbudget — 30000 bytes;details.path/details.bytes/details.threshold_bytes), andTASK_READS_MATCH_TOO_MANY(areadsglob 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 areaffects_exit: false—plan lint/plan lint --strictexit semantics are unchanged for advisory-only cases. Thresholds are deterministic byte/count values sourced fromSTANDARD_CONTEXT_BUDGET_PROFILESwhere applicable. The pass is local and deterministic: the pack-size advisories reuse the P49 explain metrics (natural_bytesand the same sharedminimum_achievable_bytesfloorCONTEXT_OVER_BUDGETuses, 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-namecontext_budgetoverride when available, else the built-in fallback — the same byte valuerecommend/task preparesurface), 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-qualityflag).task context --explain --jsongains additive Context Fit byte metrics (P49, Context Fit layer c). Alongside the unchangedtotal_bytes/context_pack_bytes/sections/excluded, an--explain --jsonbuild now reportsnatural_bytes(the pre-budget pack size),final_bytes(equal tototal_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), andelided_sections(the budget-elided sections only, in elision order).budget_bytesis present only when a budget was applied (via--budget-bytesor--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, andelided_sections === [].minimum_achievable_bytesis the same floorCONTEXT_OVER_BUDGETreports, computed by one shared helper — the success path and the error path can never disagree — honoring the P28 conditional eligibility (related_decisionselidable only whencontext_size: large;rulesonly whenwrite_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 normaltask contextoutput or ontask prepare). No tokenizer, summarization, compression, semantic ranking, embeddings, or network behavior is introduced.recommend/task preparegain an optionalcontextFitrecommendation (P48, Context Fit layer b).RecommendResultV2now carries an optional, strictly-additivecontextFit— a recommended standard context budget profile (recommendedProfile:tight/balanced/wide), itsrecommendedBudgetBytes, and a one-linereason. The profile is derived deterministically from existing task readiness fields:context_size == largeORambiguity == highORwrite_surface == high→wide; elsecontext_size == medium→balanced; elsetight.requires_decisiondoes not shrink it.recommendedBudgetBytesis an agent profile's same-namedcontext_budgetoverride when present, else the P47 built-in fallback (tight30000,balanced60000,wide120000);recommendedProfileis a closed enum and never emits a custom agent-profile name. It surfaces inrecommend --json, a one-linerecommendhuman-output entry, andtask prepare --json(underrecommendation.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 thetask preparecommandsdictionary still does not echo--context-budget.contextFitis distinct from the categoricalbudgetProfile(no overload). No token counting, tokenizer, summarization, compression, semantic ranking, embeddings, or network behavior is introduced.task context/task preparegain--context-budget <profile>(P47, Context Fit layer a). A named, ergonomic alias for a byte budget: the profile resolves to amax_bytesvalue that then drives the unchanged P24--budget-bytesenforcement path (same locked elision order, sameCONTEXT_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 optionalcontext_budgetblock to override a built-in byte value or name custom profiles;default_profileis validated but not auto-applied.--context-budgetand--budget-bytesare mutually exclusive (CONFIG_ERROR, exit 2); an unknown profile isCONFIG_ERROR. The no-flag default pack stays byte-identical,--budget-bytesis unchanged, and thetask preparecommandsdictionary does not echo--context-budget.wideis intentionally notfull— it can still elide or hitCONTEXT_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
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 --writesurfaces a remaining-advisory hint.adapter upgraderepairs generator/desired file drift but deliberately never rewrites a profile'smodel_map(a pin may be intentional), so aMODEL_MAP_STALEadvisory persists across a--write. A successful--writewith no refused files that leaves theclaude-codemodel_mappinned to a known-but-not-current catalog id now prints a human-onlyRemaining manual advisory: MODEL_MAP_STALEnote on stderr — naming the stale tier, the current default, the profile path to hand-edit, and the.code-pact/doctor.yamlsilence path. It never advises--model(which re-pinsmodel_version, notmodel_map) and never mutatesmodel_map. Scoped toclaude-code'smodel_maponly; it does not run the globaldoctoror widen to other advisories. It honors the same suppression asdoctor(a project withdisabled_checks: [MODEL_MAP_STALE]gets no hint) and is withheld when any file wasrefused(there the actionable step is--accept-modified). The--jsonenvelope is unchanged (doctor --jsonremains the machine-readable source); the hint is human-output only.
Changed
- The
MODEL_MAP_STALEcondition is now a single shared function (src/core/models/model-map-drift.ts).doctorand the newadapter upgradehint both derive staleness fromdetectModelMapDrift, so the two can never disagree about whether a profile is stale. Pure + offline. No behavior change todoctor's existingMODEL_MAP_STALE/MODEL_ID_UNKNOWNoutput. doctor.yamlloading is a single shared module (src/core/doctor-config.ts). TheDoctorConfigschema + loader moved out ofdoctor.tssodoctorand theadapter upgradehint readdisabled_checksidentically — 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)
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 themodel_versionpin all now honorproject.yaml'sagents[].profile(the pathdoctoralready used), via a singleresolveAgentProfilePathhelper. The conventionalagent-profiles/<name>.yamlremains the fallback when project.yaml is absent or the agent is unlisted; a matched agent whoseprofileis an invalid path now fails withCONFIG_ERRORrather 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 bytask prepareand the low-levelpack) wrote the pack with a rawwriteFile, so an interrupted process could leave a half-written.context/<agent>/<task-id>.md— exactly whatcli-contract.md§ "State file write guarantees" says cannot happen for the managed file-content writes it lists. It now usesatomicWriteText(temp-file + rename); the redundant explicitmkdiris 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, andagent-contract.mddistinguishtask context(read-only — builds/returns the pack) fromtask prepare/pack(the writers); a narrowcheck:doc-invariantsrule guards both the false-writer claim and the contract↔code atomic-write pairing. No new command, flag, schema field, or error code. doctorno longer nags fordesign/brief.mdon a fresh project.BRIEF_MISSINGis now gated on a real (non-TUTORIAL) phase existing, matching theCONSTITUTION_PLACEHOLDERgate. A fresh non-interactiveinitsurfacesADAPTER_MISSING+ADAPTER_STALE, notBRIEF_MISSING.getting-started.mdgained a "Whatinitcreates" table.
Maintenance (no runtime/package effect)
- Moved maintainer release evidence from
design/measurements/todocs/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
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,--modelaliases, vendor model ids, defaultmodel_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-8accepted; defaulthighest_reasoningfor the claude-code adapter is nowclaude-opus-4-8. Fixes the help example that used to failCONFIG_ERROR. - Offline model-drift doctor checks (claude-code):
MODEL_ID_UNKNOWNandMODEL_MAP_STALE— warnings, offline, scoped so codex/other agents never false-positive, suppressible via.code-pact/doctor.yaml.
Changed
- codex
model_maprefreshed to the current OpenAI Codex lineup (gpt-5.5/gpt-5.4/gpt-5.4-mini). Advisory display only; codex/geminimodel_mapis 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_STALEremediation names the profile pathdoctorevaluated.- Doc accuracy:
MISSING_MODEL_TIERis awarning; theadapter install --modeldescription reflects adaptive thinking, Opus 4.8, vendor-id aliases, andCONFIG_ERRORon unknown.
Install
npm install -g code-pact@1.29.0
Distribution
- npm:
code-pact@1.29.0(dist-taglatest) - 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
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 onlyREADME.mdas 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
CommandSpecper task subcommand (src/cli/spec/) is now the one place the flag surface is declared; parse (toParseOptions), help (renderLeafHelp), anddocs/cli-reference.generated.md(renderReference) all derive from it. All 11 task verbs ported — the task-specific hand-written help entries were removed fromsrc/cli/usage.ts. Task flag-surface drift across parse/help/reference is structurally impossible. Non-task rich help remains hand-written for now. Design indesign/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.mdbecame 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_DRIVENandCONTROL_PLANE_BRANCH_NOT_DRIVENcarry an additiverecoveryobject ({ primary, alternatives?, reference? }) alongside the unchangedmessage, so an agent can pick the next action from JSON without parsing prose. It rides throughvalidate --json(both stringify the sameDoctorResult). Scoped to the twoCONTROL_PLANE_*codes; documented underdocs/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--helpwith synopsis, options where applicable, and examples, replacing the 2-line stub.
Guardrails
Added
- Generated CLI reference check.
pnpm check:cli-reference(wired intocheck:docsand CI) fails ifdocs/cli-reference.generated.mdis stale relative to the specs. - Leaf help coverage test. Pins that every mutating / JSON-emitting
plan/phase/adaptercommand answers--helpwith 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 specificcheck-doc-invariantsrule, 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:
- shasum:
22930746f39cfcb3e5d3099bfc84c5b9e341d01f - integrity:
sha512-i5shlGBkkkAvxvbXU3pEdIVUHRLPFiio35ereO4weo/4KWsshpUdVEA07K7rWC9/mcPC7hdNtmKP/IZT0dzleA== - tarball: https://registry.npmjs.org/code-pact/-/code-pact-1.28.0.tgz
Verified by downloading the published tarball and recomputing shasum (matches the registry value above).
v1.26.0 — Reshape roadmap P32–P37 + identifier/path hardening
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, roadmapPhaseRef.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--agentofrecommend/pack; the write paths runPhase.parse/PhaseRef.parsebefore 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_dirandAgentRef.profileare 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, thedesign/decisions/design/rulesreaddir loaders, and the.context/<agent>output directory. Theacceptance_refsexistence checks and the--baselinename are likewise constrained.
Fixed
CONTEXT_OVER_BUDGETenvelope.task context/task prepare --budget-bytesnow placebudget_bytes/minimum_achievable_bytes/unelidable_sectionsunder a top-leveldata(matching the documented envelope convention anddoctor/validate), not undererror.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 outcomecommand, nooutcomeschema 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 fromprogress.yamltoday. The reshape closes at P36; the next step is real-world dogfooding and release prep. Rationale + revisit conditions indesign/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 singleplan lint --include-qualityadvisory (warning,affects_exit: false) that flags anacceptedADR 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 belowADR_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:** acceptedline is in scope; a 0-byte empty file and proposed/draft ADRs are not. Advisory only — thetask complete/verifydecision 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>ondoctor/validateand a new advisoryCONTROL_PLANE_BRANCH_NOT_DRIVEN. The check runs only when--base-refis supplied and compares the branch diff (merge-base..HEAD, via the existingauditWritesmerge-base mode). It fires when real, non-excluded files changed but the branch added no event that isstarted/doneAND non-TUTORIAL AND atask_idpresent in the loaded plan — so merely touchingprogress.yaml(or appending an unrelated/unknown/TUTORIAL event) does not pass. Astartedordonefor a known task suppresses it (usage detection, not completion guarantee).- Gate model. Advisory (
severity: warning) by default — plaindoctor/validateexit unchanged.validate --strict --base-ref origin/mainpromotes 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.yamlcontrol_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.yamlis not git-tracked (the ledger must be committed for CI to audit it), or HEADprogress.yamlis unparseable (INVALID_YAML/SCHEMA_ERRORowns that). docs ship a version-pinned GitHub Actions example; noci scaffoldcommand.
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
datafields on thetask complete/task finalizefailure envelopes —failed_checks: string[],first_failure: { name, reason } | null, andsuggested_next_command: string | null, placed alongside the unchangeddata.verify.checks/data.write_audit.suggested_next_commandis 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-runfailures carry the same fields (verification runs before the dry-run short-circuit).task finalizesynthesizes 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 arerun after fixing:line (ja:原因:/修正後に再実行:) are printed to stderr. The label is deliberately notnext:, so an agent does not read it as "just rerun unchanged".
Internal
- New pure helper
src/core/failure/failure-summary.ts(no dependency onsrc/commands/; takes a structuralFailureCheckLike) and a shared CLI renderersrc/cli/render/failure-summary.ts, reused by bothtask completeandtask 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
lifecycleModeonrecommend/task prepare— an additive field (full_loop|record_only|decision_loop) from a conservative deterministic switch:decision_loopwhen the task or phaserequires_decision;record_onlywhentype ∈ {docs, test}ANDambiguity == lowANDrisk == lowANDverification_strength == strong; otherwisefull_loop.architectureis not auto-decision_loop. Advisory only — code-pact's owntask complete/task record-donebehavior is unchanged.- Recommendation-consumption guidance in generated adapters (both locales) — the "What to verify first" axis now tells agents to read
data.recommendationas an execution profile (tier/effort/planningRequired/lifecycleMode), to report a limitation when the runtime cannot switch model, and thatrecord_onlyis a lighter loop — not lighter verification (run the project verification commands, thentask record-done --evidence). - Three version-gated
adapter conformancechecks —recommendation_consumption_guidance_present,lifecycle_mode_guidance_present,cannot_switch_model_fallback_presentverify the guidance is present (anchored on short stable tokens). Gated on a newRECOMMENDATION_CONSUMPTION_FROM_VERSIONthreshold (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, reusesisDecisionRequiredForTask);resolveRecommendationtakes a meaning-closeddecisionContext. Noagent_actionJSON field (it would duplicate the prose guidance without adding enforcement).
Integrity
Published npm tarball (code-pact@1.26.0), value...