Package Changelog
Minor Changes
-
3be42f8: Add: a pack manifest schema, a local pack resolver, and a
cadence doctorcheck 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 ingatesFororeffectiveGateSetreads a pack yet. Nothing about which gates fire, for any profile × tier, changes in this release.@thomas-powers-jr/cadence-typesgainsPackManifestZ, the inferredPackManifesttype,PACK_ID_GRAMMAR, andisValidPackId. A pack id follows a single<scope>/<name>grammar mirroring npm scoping (internal packs use thecadencescope;@scope/nameis 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 noremove,override, orsetkey anywhere — a pack'sgates[]entries carry anaddarray and nothing else, so there is no loosening shape to leave unenforced by mistake (I-2/I-3,dec-20260822-018).isValidPackIdis exported separately because a config-supplied id never passes throughPackManifestZ(that validates the manifest's ownidfield, a different string), so a resolver needs a way to reject a malformedpacks.enabledentry before it reaches a filesystem path join.@thomas-powers-jr/cadence-coregainsresolvePacks(repoRoot, config)and itsResolvedPackresult 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 bothpacks.enabledandpacks.disabledare excluded entirely before resolution — disabled wins, the tighten-only principle (D-AQ).sourceis 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
packsdoctor check surfaces all of this to the operator. Zero enabled packs reportsokwith no warning, as does a list whose ids are all disabled; when every enabled pack resolves it reportsoknaming each one. Any unresolved pack makes itwarning— nevererror— 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 indec-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 classifiedmanualforcadence doctor --fixand carries nofixId: fabricating a manifest would invent content the operator never authored, and dropping the id frompacks.enabledwould silently disable exactly what they installed it to get.Closes
rec-20260822-009. -
d295ceb: Add: a pack's
skillAudit.requiredis now a real behavioral contributor to the skill-audit check, with per-requirement provenance recorded inSUMMARY.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/effectiveGateSetremain free of anypacks/import, proven by the structural no-coupling test phase 290 added — a manifest'sgates[].adddeltas are still slice 3.runSkillAuditCheck(@thomas-powers-jr/cadence-core) now unions each successfully-resolved enabled pack'sskillAudit.requiredintoeffectiveRequiredalongsideconfig.skillAudit.requiredand the DRAFT'srequiredSkills. A pack-declared required skill that telemetry never recorded as invoked refusescadence settle runexactly like a config-declared one, honors the same--allow-skill-audit-missbypass, 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-typesgains an additive, optionalprovenance: { skill, source }[]on bothCadenceStateZ.skillAuditandSummaryZ'sskillAuditobject —.optional()with no.default(...), mirroring thecoverageScheme/providerSelectionprecedent, so a pre-slice-2SUMMARY.jsonstill parses with the key genuinely absent and its content hash still verifies after a schema round-trip.sourceisconfig,draft, orpack:<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 theGateenum 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 dedicatedcadence settle run --allow-unresolvable-pack, which records the bypass as a realSUMMARY.gateBypassesentry (gatepack-resolution, severitywarn) — unlike--allow-skill-audit-miss, which only ever emits abypassed: truewarn anomaly and never reachesgateBypasses.Because packs are now behaviorally consumed,
cadence doctor'spackscheck escalates from slice 1's deliberatewarningtoerror, completing the two-phase plandec-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 stillok, and an unloadableconfig.jsonstill degrades tookrather than double-reporting whatcheckInitializedalready flags. The check remainsmanualforcadence doctor --fixwith nofixId.Closes
rec-20260822-010. -
08c42df: Add: a pack's
gates[].addis 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 requiredresolvedPacks: 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'sgates[].addentries into its output whenever that entry's(profile, tier)matches the active cell, deduped the same waygatesForalready 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), andnotify/loop-violation.ts.gatesForitself is untouched — still the raw, packs-free two-argument(tier, profile)matrix builder, and still the functiondoctor/run.ts's reachability scan andconfig-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'sgates[].addmatches the active(profile, tier)cell and actually adds a gate not already present in the rawgatesFor()output for that cell, the row includes it and a newpacks-augment-current-tierwarning 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 fromgatesFor.gatherExplainContextnow takes the already-loaded config as a parameter (rather than reloading it) so it resolves packs from the exact sameconfig.packsthe rest of the command run is using.A pack manifest's
gates[]shape stays additive-only — there is still noremove/override/setkey anywhere inPackGateDeltaZ(.strict(), unchanged from slice 1) — and a non-additive shape (an unrecognized key alongside a validaddarray) is regression-tested as rejected at parse time, not silently ignored or silently dropped.No
@thomas-powers-jr/cadence-typeschange in this release — the manifest schema (PackGateDeltaZ,PackManifestZ) was already additive-only as of slice 1; slice 3 only wires the existing schema'sgates[].addfield into gate computation, in@thomas-powers-jr/cadence-core.Closes
rec-20260822-011. -
bf37072: Add:
cadence doctorgains a newpack-commandscheck verifying each enabled pack's declaredcommands[]entries (slash-command names,docs/packs-design.md§3) name a key ofCOMMAND_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 noGate/DELTASentry and no refusal path, and severity never escalates pastwarning— unlike thepackscheck's slice-1-to-slice-2 escalation, this rung is permanent.checkPackCommands(packages/core/src/doctor/run.ts) mirrorscheckPacks's shape exactly:loadConfig+resolvePacks, degrading topasson any config-load failure, and branching on the resolved pack list rather thanconfig.packs.enabled.lengthso an id listed in bothpacks.enabledandpacks.disabledis excluded before the check runs (disabled wins, D-AQ). An absent or emptycommandsfield is clean, not a finding. When any resolved pack declares acommands[]entry that isn't aCOMMAND_GUIDANCEkey, the check reportswarningnaming the pack id and every unrecognized command, withfixId: 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 inpackages/host-toolkit/tests/routing.test.tsassertsCOMMANDS.map(c => c.name)(frompackages/host-toolkit/src/routing.ts) equalsObject.keys(COMMAND_GUIDANCE)exactly, so a command added to one catalog but not the other is caught immediately instead of lettingpack-commandssilently 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
- npm publish completed with provenance in the Release workflow.
- Remote tag
v1.66.0is verified before the GitHub Release is created. - npm package versions and GitHub Release metadata are verified after publish.
- Workflow run: https://github.com/thomas-powers-jr/cadence/actions/runs/32654442112