Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help


id: packs kind: intent checked_against_core: clear statement: “WHEN a workspace enables a domain pack, THE format SHALL make the extension mechanism first-class — a kind: profile spec file whose six per-facet manifest tables declare exactly the sections, namespaced kinds, reference fields, checkers, floors, and requirements the pack introduces — and SHALL activate pack checking advisory-first by vocabulary use or a declared uses edge, never by config files, never by narrowing any base closed set, and never by changing how files without packs lint.”

Packs

Make specodelic’s extension mechanism first-class: a domain pack is a declared, discoverable, versioned artifact in the corpus itself — not a convention followed by hand. Base closed sets stop growing; vocabulary is pack-fiber-relative (the Grothendieck construction one level up, per [[theory]] precedent); independent packs coexist namespaced instead of colliding in the global kind sets.

Purpose

Make specodelic’s extension mechanism first-class: a domain pack is a declared, discoverable, versioned artifact in the corpus itself — not a convention followed by hand. Base closed sets stop growing; vocabulary is pack-fiber-relative (the Grothendieck construction one level up, per [[theory]] precedent); independent packs coexist namespaced instead of colliding in the global kind sets.

Constraints

idkindexprtraces_to
pack_is_profile_fileinvarianta pack is a four-layer spec file with kind: profile in frontmatter — no separate artifact format, no external manifest, self-hosting preserved[[packs]]
manifest_six_tablesinvarianta pack manifest is exactly six per-facet closed tables — ## Sections, ## Kinds, ## References, ## Checkers, ## Floors, ## Requires — each with a fixed row shape checked by the pack_shape lint rule[[packs]]
discovery_corpus_scaninvariantevery kind: profile file in the workspace is a candidate pack, found by corpus scan alone anchored at the git repository toplevel (the lint target directory when no git root exists) — no config file, no registry outside the corpus, and a subdirectory lint discovers packs living anywhere in the workspace[[packs]]
namespaced_vocabularyinvariantpack-declared kinds, sections, reference fields, and predicate names are pack-qualified in fiber scope; two packs claiming the same mechanism is legitimate, collision exists only at vocabulary level and resolves by pack-qualified naming[[packs]]
base_closed_sets_frozeninvariantbase closed sets (constraint kinds, property kinds, intent kinds, reference typing) stop growing except by true format Revision; a pack can never narrow or retype an existing set entry[[packs]]
advisory_first_opt_ininvariantpack checking activates advisory-first when a file uses the pack's declared vocabulary, upgradeable to a declared uses edge; activation is per-pack — identical vocabulary declared by two discovered packs activates both with findings attributed per pack — and vocabulary matching is longest-prefix; files without packs lint byte-identically to pre-mechanism behavior[[packs]]
orphan_vocabulary_labeledinvariantdeclared vocabulary used with no pack discovered or declared produces a labeled failure finding (non-zero exit) naming the candidate pack — prefix-derived when only the namespace is known — and both remediations; never a generic dangling message, never silent[[packs]]
uses_is_outbound_leafinvariantthe uses reference field appears as a set-valued column on any Constraint row and resolves to the intent of a kind: profile file (the pack's frontmatter id); it is an outbound leaf joining no reachability path and no acyclic edge set — nothing targets it, single_root_reachable needs no carve-out, mutual pack use is well-formed[[packs]]
pack_self_exemptinvarianta pack file is never a consumer of declared vocabulary — its own manifest rows never trigger its or any pack's checkers and never produce orphan findings[[packs]]
lifecycle_labeledinvariantpack states are draft, published, deprecated; pack_shape must pass at parse in every state and draft packs' checkers activate advisory-first naming the draft status; the pack-authored ## Requires table pins the base format_revision and pack deps; deprecated packs stay advisory with deprecation named in findings[[packs]]
skew_advisoryinvariantrevision skew between a pack's Requires base pin and the workspace corpus revision is a labeled advisory on the warnings channel — never silent, never failing[[packs]]
honest_emptyinvarianta pack checker with no workspace fixtures, data, or vocabulary instances reports an empty checked-set honestly — no fabricated discoveries[[packs]]
append_only_packsinvariantappend_only_variants applies to packs themselves — a pack release may only add vocabulary, never remove or narrow previously declared entries[[packs]]

Model

States

  • draft
  • published
  • deprecated

Transitions

idfromtoguard
publishdraftpublished[[packs.pack_is_profile_file]] ∧ [[packs.manifest_six_tables]] — pack_shape reports zero findings over the pack's own declared vocabulary
deprecatepublisheddeprecated[[packs.lifecycle_labeled]] — maintainer marks the pack deprecated; findings name the deprecation, vocabulary still checks

Properties

idkindderives_fromgeneratorpredicate
manifest_round_tripsunit[[packs.manifest_six_tables]]pack_file_parsed_then_manifest_read()declared entries equal parsed manifest rows — no drift, no prose leakage
discovery_finds_all_packsunit[[packs.discovery_corpus_scan]]corpus_with_n_profile_files()scan reports exactly n packs, order-independent
subdir_lint_discovers_workspace_packsunit[[packs.discovery_corpus_scan]]lint_subdirectory_with_pack_at_workspace_root()pack discovered, its vocabulary checks run, zero orphan findings
no_pack_no_changeunit[[packs.advisory_first_opt_in]]corpus_linted_before_and_after_mechanism()lint findings byte-identical for a corpus using no pack vocabulary
overlapping_vocabulary_activates_bothunit[[packs.advisory_first_opt_in]]two_packs_declare_identical_vocabulary_and_a_file_uses_it()both packs' checkers run; findings attributed per pack
orphan_finding_names_packunit[[packs.orphan_vocabulary_labeled]]corpus_using_vocabulary_without_pack()failure finding names the candidate pack and both remediations, exit non-zero
uses_edge_typingunit[[packs.uses_is_outbound_leaf]]file_with_uses_edge_to_profile_id()graph edge typed uses resolves to the profile id; wrong-kind targets violate typing
pack_self_activation_absentunit[[packs.pack_self_exempt]]pack_file_linted_alone()its checkers report an empty checked-set; no orphan findings from the pack's own manifest rows
skew_is_warning_not_failureunit[[packs.skew_advisory]]pack_pinned_to_older_revision()exit 0 with warning-channel advisory naming the pack's base pin and the corpus revision
empty_vocabulary_honestunit[[packs.honest_empty]]pack_enabled_without_instances()checker reports empty checked-set; no fabricated findings
narrowing_rejectedunit[[packs.append_only_packs]]pack_removing_previously_declared_kind()pack_shape fails naming the removed entry and the append-only law
profile_file_is_four_layerunit[[packs.pack_is_profile_file]]pack_parsed_as_standard_spec()all four layers parse from the pack file alone — no sidecar artifact needed
pack_vocabulary_qualifiedunit[[packs.namespaced_vocabulary]]two_packs_claim_same_mechanism()both packs' declared vocabulary resolves without cross-pack collision
base_sets_unchanged_by_packsunit[[packs.base_closed_sets_frozen]]guide_sets_compared_across_pack_enable()INTENT_KINDS and Reference Typing sets identical with and without packs discovered
lifecycle_states_parsedunit[[packs.lifecycle_labeled]]pack_model_section_parsed()each lifecycle state yields exactly its declared checking behavior
fiber_kinds_typeableunit[[packs.namespaced_vocabulary]]base_table_kind_column_with_active_fiber_kind()the base closed-set walkers accept the pack-qualified kind when the pack is active; the same token without the pack fires the labeled finding (base ∪ active-pack-fiber, never narrower)

Notes

Fiber kinds in base-table kind columns (2026-10-03, beads specodelic-ung): mechanism v1’s closed-set walkers (property_kind_closed / constraint_kind_closed, openspec/specs/linter-schema_shape/spec.md) walked the static base sets, so a pack-declared fiber kind in a base-table kind column fired the finding even with its pack discovered — the sibling packs never hit this because their kinds live in pack-added sections. The walkers’ effective set is now base ∪ active-pack-fiber: the ## Kinds tokens of every pack active for the file (declared uses edge or vocabulary match — the same advisory-first activation rule as everything else here). This completes namespaced_vocabulary: a fiber kind is typeable in fiber scope, per-file. The base sets themselves do not grow (no format Revision; base pins stay valid) and a fiber kind with no active declaring pack stays outside the closed set — the labeled finding fires, never a silent pass. First consumer: empirical.registry’s empirical.statistic (the empirical-registry-pack capability spec’s fiber_kinds_typeable constraint and kind_column_accepts_fiber_kinds property).

Requirements

Requirement: Property coverage mirror

Every property row SHALL be verified by exactly one dedicated scenario; ah sync SHALL derive one contract per VERIFIES link.

Scenario: manifest_round_trips

  • WHEN pack_file_parsed_then_manifest_read()
  • THEN declared entries equal parsed manifest rows — no drift, no prose leakage
  • VERIFIES [[packs.manifest_round_trips]]

Scenario: discovery_finds_all_packs

  • WHEN corpus_with_n_profile_files()
  • THEN scan reports exactly n packs, order-independent
  • VERIFIES [[packs.discovery_finds_all_packs]]

Scenario: subdir_lint_discovers_workspace_packs

  • WHEN lint_subdirectory_with_pack_at_workspace_root()
  • THEN pack discovered, its vocabulary checks run, zero orphan findings
  • VERIFIES [[packs.subdir_lint_discovers_workspace_packs]]

Scenario: no_pack_no_change

  • WHEN corpus_linted_before_and_after_mechanism()
  • THEN lint findings byte-identical for a corpus using no pack vocabulary
  • VERIFIES [[packs.no_pack_no_change]]

Scenario: overlapping_vocabulary_activates_both

  • WHEN two_packs_declare_identical_vocabulary_and_a_file_uses_it()
  • THEN both packs' checkers run; findings attributed per pack
  • VERIFIES [[packs.overlapping_vocabulary_activates_both]]

Scenario: orphan_finding_names_pack

  • WHEN corpus_using_vocabulary_without_pack()
  • THEN failure finding names the candidate pack and both remediations, exit non-zero
  • VERIFIES [[packs.orphan_finding_names_pack]]

Scenario: uses_edge_typing

  • WHEN file_with_uses_edge_to_profile_id()
  • THEN graph edge typed uses resolves to the profile id; wrong-kind targets violate typing
  • VERIFIES [[packs.uses_edge_typing]]

Scenario: pack_self_activation_absent

  • WHEN pack_file_linted_alone()
  • THEN its checkers report an empty checked-set; no orphan findings from the pack's own manifest rows
  • VERIFIES [[packs.pack_self_activation_absent]]

Scenario: skew_is_warning_not_failure

  • WHEN pack_pinned_to_older_revision()
  • THEN exit 0 with warning-channel advisory naming the pack's base pin and the corpus revision
  • VERIFIES [[packs.skew_is_warning_not_failure]]

Scenario: empty_vocabulary_honest

  • WHEN pack_enabled_without_instances()
  • THEN checker reports empty checked-set; no fabricated findings
  • VERIFIES [[packs.empty_vocabulary_honest]]

Scenario: narrowing_rejected

  • WHEN pack_removing_previously_declared_kind()
  • THEN pack_shape fails naming the removed entry and the append-only law
  • VERIFIES [[packs.narrowing_rejected]]

Scenario: profile_file_is_four_layer

  • WHEN pack_parsed_as_standard_spec()
  • THEN all four layers parse from the pack file alone — no sidecar artifact needed
  • VERIFIES [[packs.profile_file_is_four_layer]]

Scenario: pack_vocabulary_qualified

  • WHEN two_packs_claim_same_mechanism()
  • THEN both packs' declared vocabulary resolves without cross-pack collision
  • VERIFIES [[packs.pack_vocabulary_qualified]]

Scenario: base_sets_unchanged_by_packs

  • WHEN guide_sets_compared_across_pack_enable()
  • THEN INTENT_KINDS and Reference Typing sets identical with and without packs discovered
  • VERIFIES [[packs.base_sets_unchanged_by_packs]]

Scenario: lifecycle_states_parsed

  • WHEN pack_model_section_parsed()
  • THEN each lifecycle state yields exactly its declared checking behavior
  • VERIFIES [[packs.lifecycle_states_parsed]]

Scenario: fiber_kinds_typeable

  • WHEN base_table_kind_column_with_active_fiber_kind()
  • THEN the base closed-set walkers accept the pack-qualified kind when the pack is active; the same token without the pack fires the labeled finding (base ∪ active-pack-fiber, never narrower)
  • VERIFIES [[packs.fiber_kinds_typeable]]