Skip to content

v1.66.0

Latest

Choose a tag to compare

@github-actions github-actions released this 23 Aug 17:26
· 2 commits to main since this release
1314ac6

Package Changelog

Minor Changes

  • 3be42f8: Add: a pack manifest schema, a local pack resolver, and a cadence doctor check reporting whether each enabled pack actually resolves — with zero behavioral effect on gate computation. This is slice 1 of the packs arc (docs/packs-design.md): it proves invariants I-1, I-2, and I-4a are expressible and enforced at the type/resolution layer while nothing anywhere in gatesFor or effectiveGateSet reads a pack yet. Nothing about which gates fire, for any profile × tier, changes in this release.

    @thomas-powers-jr/cadence-types gains PackManifestZ, the inferred PackManifest type, PACK_ID_GRAMMAR, and isValidPackId. A pack id follows a single <scope>/<name> grammar mirroring npm scoping (internal packs use the cadence scope; @scope/name is reserved for third-party packs later, with no special-casing — I-1). The manifest schema is .strict() at every level, so it fails closed on any unrecognized key rather than silently ignoring it, and it deliberately has no remove, override, or set key anywhere — a pack's gates[] entries carry an add array and nothing else, so there is no loosening shape to leave unenforced by mistake (I-2/I-3, dec-20260822-018). isValidPackId is exported separately because a config-supplied id never passes through PackManifestZ (that validates the manifest's own id field, a different string), so a resolver needs a way to reject a malformed packs.enabled entry before it reaches a filesystem path join.

    @thomas-powers-jr/cadence-core gains resolvePacks(repoRoot, config) and its ResolvedPack result type. Resolution is local-only — .cadence/packs/<id>/pack.json, project-local, git-tracked, zero network (D-AR; registry/remote sources are an explicit non-goal for this whole arc). It never throws: a missing file, malformed JSON, a schema-validation failure, and a grammar-invalid id each come back as a per-pack { id, source, error } result, and one pack's failure never blocks another's resolution. Ids appearing in both packs.enabled and packs.disabled are excluded entirely before resolution — disabled wins, the tighten-only principle (D-AQ). source is assigned by the resolver, never self-declared by the manifest, so a pack cannot make a self-authorizing claim about its own provenance.

    The new packs doctor check surfaces all of this to the operator. Zero enabled packs reports ok with no warning, as does a list whose ids are all disabled; when every enabled pack resolves it reports ok naming each one. Any unresolved pack makes it warning — never error — with a detail that names both the unresolved ids with their reasons and the ids that did resolve. The warning severity is deliberate and recorded as a two-phase plan in dec-20260822-025 / docs/packs-design.md §6 D-AR: nothing consumes packs behaviorally yet, so an unresolved pack breaks nothing today, and escalating it to a hard settle-time refusal is the job of the later slice that actually makes packs behaviorally consumed (following v1.64.0's "fail loud instead of passing every gate vacuously" precedent). The check is classified manual for cadence doctor --fix and carries no fixId: fabricating a manifest would invent content the operator never authored, and dropping the id from packs.enabled would silently disable exactly what they installed it to get.

    Closes rec-20260822-009.

  • d295ceb: Add: a pack's skillAudit.required is now a real behavioral contributor to the skill-audit check, with per-requirement provenance recorded in SUMMARY.json — and an enabled pack that fails to resolve now refuses settle instead of being silently ignored. This is slice 2 of the packs arc (docs/packs-design.md), the first slice where a resolved pack does anything. Gate computation is still untouched: gatesFor/effectiveGateSet remain free of any packs/ import, proven by the structural no-coupling test phase 290 added — a manifest's gates[].add deltas are still slice 3.

    runSkillAuditCheck (@thomas-powers-jr/cadence-core) now unions each successfully-resolved enabled pack's skillAudit.required into effectiveRequired alongside config.skillAudit.required and the DRAFT's requiredSkills. A pack-declared required skill that telemetry never recorded as invoked refuses cadence settle run exactly like a config-declared one, honors the same --allow-skill-audit-miss bypass, and takes the same warn path when skill telemetry is off. The null-config path is unchanged: when config fails to load, resolved packs contribute nothing and settle still computes the narrower set without enforcing it, so no repo gains a false refusal from this release.

    @thomas-powers-jr/cadence-types gains an additive, optional provenance: { skill, source }[] on both CadenceStateZ.skillAudit and SummaryZ's skillAudit object — .optional() with no .default(...), mirroring the coverageScheme/providerSelection precedent, so a pre-slice-2 SUMMARY.json still parses with the key genuinely absent and its content hash still verifies after a schema round-trip. source is config, draft, or pack:<id>, and entries are per (skill, source) pair rather than collapsed: a skill required by both config and a pack yields two entries, so the record says where each requirement actually came from instead of erasing it (D-AS).

    An enabled-but-unresolvable pack is now a hard settle-time refusal. The new checkUnresolvablePacks (packages/core/src/checks/pack-resolution.ts) is a sibling of the skill-audit check, dispatched explicitly by settle and deliberately outside the Gate enum and the profile × tier matrix — enabling a pack is the opt-in, so there is no gate to switch on. It runs before the skill-audit check, so settle refuses on the right grounds rather than computing a skill-audit "pass" from a pack whose manifest never loaded. The refusal names each unresolvable id with its reason and is bypassable only via the new dedicated cadence settle run --allow-unresolvable-pack, which records the bypass as a real SUMMARY.gateBypasses entry (gate pack-resolution, severity warn) — unlike --allow-skill-audit-miss, which only ever emits a bypassed: true warn anomaly and never reaches gateBypasses.

    Because packs are now behaviorally consumed, cadence doctor's packs check escalates from slice 1's deliberate warning to error, completing the two-phase plan dec-20260822-025 / docs/packs-design.md §6 D-AR recorded up front. An unresolved enabled pack silently drops enforcement the operator installed it to get and will refuse their next settle, so doctor no longer reports that repo as healthy. Zero enabled packs, an all-disabled list, and a fully-resolving set are all still ok, and an unloadable config.json still degrades to ok rather than double-reporting what checkInitialized already flags. The check remains manual for cadence doctor --fix with no fixId.

    Closes rec-20260822-010.

  • 08c42df: Add: a pack's gates[].add is now a real behavioral contributor to gate computation. This is slice 3 of the packs arc (docs/packs-design.md) — the slice that finally makes a manifest's gate-profile deltas do something.

    effectiveGateSet (packages/core/src/gates/engine.ts) now takes a required resolvedPacks: ResolvedPack[] parameter (no default, so a missed call site is a compile error, not a silent no-op) and unions each successfully-resolved enabled pack's gates[].add entries into its output whenever that entry's (profile, tier) matches the active cell, deduped the same way gatesFor already dedups. All nine real call sites were updated to pass it: draft-check, draft-approve, build-task (twice), settle, hooks/handlers.ts (three call sites), and notify/loop-violation.ts. gatesFor itself is untouched — still the raw, packs-free two-argument (tier, profile) matrix builder, and still the function doctor/run.ts's reachability scan and config-explain/build.ts's whole-matrix table correctly keep calling directly, because both of those answer a matrix-wide question ("what's reachable/configured at any tier") rather than "what applies to my phase right now."

    cadence config explain's current-tier row now reflects enabled packs' gate contributions: when a pack's gates[].add matches the active (profile, tier) cell and actually adds a gate not already present in the raw gatesFor() output for that cell, the row includes it and a new packs-augment-current-tier warning names which pack added which gate. Every other row in the tier × profile matrix table stays raw, unconditionally — only the current-tier row can diverge from gatesFor. gatherExplainContext now takes the already-loaded config as a parameter (rather than reloading it) so it resolves packs from the exact same config.packs the rest of the command run is using.

    A pack manifest's gates[] shape stays additive-only — there is still no remove/override/set key anywhere in PackGateDeltaZ (.strict(), unchanged from slice 1) — and a non-additive shape (an unrecognized key alongside a valid add array) is regression-tested as rejected at parse time, not silently ignored or silently dropped.

    No @thomas-powers-jr/cadence-types change in this release — the manifest schema (PackGateDeltaZ, PackManifestZ) was already additive-only as of slice 1; slice 3 only wires the existing schema's gates[].add field into gate computation, in @thomas-powers-jr/cadence-core.

    Closes rec-20260822-011.

  • bf37072: Add: cadence doctor gains a new pack-commands check verifying each enabled pack's declared commands[] entries (slash-command names, docs/packs-design.md §3) name a key of COMMAND_GUIDANCE (@thomas-powers-jr/cadence-types) — the registered slash-command set. This is slice 4 of the packs arc (docs/packs-design.md) and, per D-AP, is deliberately doctor-checked only, never enforced: there is no Gate/DELTAS entry and no refusal path, and severity never escalates past warning — unlike the packs check's slice-1-to-slice-2 escalation, this rung is permanent.

    checkPackCommands (packages/core/src/doctor/run.ts) mirrors checkPacks's shape exactly: loadConfig + resolvePacks, degrading to pass on any config-load failure, and branching on the resolved pack list rather than config.packs.enabled.length so an id listed in both packs.enabled and packs.disabled is excluded before the check runs (disabled wins, D-AQ). An absent or empty commands field is clean, not a finding. When any resolved pack declares a commands[] entry that isn't a COMMAND_GUIDANCE key, the check reports warning naming the pack id and every unrecognized command, with fixId: null — inventing or removing a pack's declared commands is not a safe automatic repair.

    The check's authority for "does this command exist" — Object.keys(COMMAND_GUIDANCE) — is pinned against the actual installed-slash-command catalog: a new test in packages/host-toolkit/tests/routing.test.ts asserts COMMANDS.map(c => c.name) (from packages/host-toolkit/src/routing.ts) equals Object.keys(COMMAND_GUIDANCE) exactly, so a command added to one catalog but not the other is caught immediately instead of letting pack-commands silently pass or falsely warn.

    Closes rec-20260822-012.

Patch Changes

  • Updated dependencies [3be42f8]
  • Updated dependencies [d295ceb]
    • @thomas-powers-jr/cadence-types@1.66.0

Published Packages

All public packages are published on npm as 1.66.0:

  • @thomas-powers-jr/cadence-core
  • @thomas-powers-jr/cadence-host-claude-code
  • @thomas-powers-jr/cadence-host-codex
  • @thomas-powers-jr/cadence-host-toolkit
  • @thomas-powers-jr/cadence-types

Verification