@ybotok/loam 0.1.0-beta.3 → 0.2.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2635 -86
- package/COMPARISON.md +80 -63
- package/CONTRIBUTING.md +84 -0
- package/MIGRATING-from-OpenSpec.md +213 -54
- package/QUICKSTART.md +125 -0
- package/README.md +450 -150
- package/ROADMAP.md +599 -0
- package/SCHEMA.md +2355 -256
- package/SECURITY.md +21 -3
- package/WORKFLOW.md +701 -0
- package/dist/cli.js +113 -19
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +209 -0
- package/dist/commands/adopt/adopt.js.map +1 -0
- package/dist/commands/adopt/render.js +268 -0
- package/dist/commands/adopt/render.js.map +1 -0
- package/dist/commands/archive/archive.js +77 -0
- package/dist/commands/archive/archive.js.map +1 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +159 -0
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +323 -0
- package/dist/commands/archive/plan/gate.js.map +1 -0
- package/dist/commands/archive/plan/landscape.js +180 -0
- package/dist/commands/archive/plan/landscape.js.map +1 -0
- package/dist/commands/archive/plan/refusal.js +111 -0
- package/dist/commands/archive/plan/refusal.js.map +1 -0
- package/dist/commands/archive/plan/specs.js +237 -0
- package/dist/commands/archive/plan/specs.js.map +1 -0
- package/dist/commands/archive/plan/state.js +27 -0
- package/dist/commands/archive/plan/state.js.map +1 -0
- package/dist/commands/archive/run.js +252 -0
- package/dist/commands/archive/run.js.map +1 -0
- package/dist/commands/context/context.js +99 -0
- package/dist/commands/context/context.js.map +1 -0
- package/dist/commands/context/print.js +283 -0
- package/dist/commands/context/print.js.map +1 -0
- package/dist/commands/delta/delta.js +279 -0
- package/dist/commands/delta/delta.js.map +1 -0
- package/dist/commands/delta/print.js +205 -0
- package/dist/commands/delta/print.js.map +1 -0
- package/dist/commands/dependencies.js +9 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +93 -0
- package/dist/commands/diff/diff.js.map +1 -0
- package/dist/commands/diff/print.js +38 -0
- package/dist/commands/diff/print.js.map +1 -0
- package/dist/commands/doctor.js +5 -4
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain/explain.js +151 -0
- package/dist/commands/explain/explain.js.map +1 -0
- package/dist/commands/explore.js +29 -24
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +115 -0
- package/dist/commands/gate/gate.js.map +1 -0
- package/dist/commands/gate/print.js +54 -0
- package/dist/commands/gate/print.js.map +1 -0
- package/dist/commands/gherkin/commit.js +146 -0
- package/dist/commands/gherkin/commit.js.map +1 -0
- package/dist/commands/gherkin/gherkin.js +246 -0
- package/dist/commands/gherkin/gherkin.js.map +1 -0
- package/dist/commands/gherkin/reconcile.js +149 -0
- package/dist/commands/gherkin/reconcile.js.map +1 -0
- package/dist/commands/gherkin/render.js +77 -0
- package/dist/commands/gherkin/render.js.map +1 -0
- package/dist/commands/init/example.js +204 -0
- package/dist/commands/init/example.js.map +1 -0
- package/dist/commands/init/first-hour.js +28 -0
- package/dist/commands/init/first-hour.js.map +1 -0
- package/dist/commands/{init.js → init/init.js} +146 -71
- package/dist/commands/init/init.js.map +1 -0
- package/dist/commands/init/mcp.js +131 -0
- package/dist/commands/init/mcp.js.map +1 -0
- package/dist/commands/init/options.js +78 -0
- package/dist/commands/init/options.js.map +1 -0
- package/dist/commands/instructions.js +72 -14
- package/dist/commands/instructions.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +65 -0
- package/dist/commands/list/campaign/campaign.js.map +1 -0
- package/dist/commands/list/campaign/owners.js +65 -0
- package/dist/commands/list/campaign/owners.js.map +1 -0
- package/dist/commands/list/json.js +157 -0
- package/dist/commands/list/json.js.map +1 -0
- package/dist/commands/list/list.js +343 -0
- package/dist/commands/list/list.js.map +1 -0
- package/dist/commands/list/print.js +301 -0
- package/dist/commands/list/print.js.map +1 -0
- package/dist/commands/list/review.js +120 -0
- package/dist/commands/list/review.js.map +1 -0
- package/dist/commands/list/views.js +105 -0
- package/dist/commands/list/views.js.map +1 -0
- package/dist/commands/mcp/dispatch.js +183 -0
- package/dist/commands/mcp/dispatch.js.map +1 -0
- package/dist/commands/mcp/mcp.js +34 -0
- package/dist/commands/mcp/mcp.js.map +1 -0
- package/dist/commands/mcp/serve.js +55 -0
- package/dist/commands/mcp/serve.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/active.js +170 -0
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
- package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/living.js +62 -0
- package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/target.js +219 -0
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/error.js +19 -0
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/read.js +49 -0
- package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
- package/dist/commands/migrate-openspec/print.js +89 -0
- package/dist/commands/migrate-openspec/print.js.map +1 -0
- package/dist/commands/new/commit.js +76 -0
- package/dist/commands/new/commit.js.map +1 -0
- package/dist/commands/new/new.js +283 -0
- package/dist/commands/new/new.js.map +1 -0
- package/dist/commands/new/templates.js +355 -0
- package/dist/commands/new/templates.js.map +1 -0
- package/dist/commands/open.js +203 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/policy/format.js +152 -0
- package/dist/commands/policy/format.js.map +1 -0
- package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
- package/dist/commands/policy/gate.js.map +1 -0
- package/dist/commands/rebase/edit.js +106 -0
- package/dist/commands/rebase/edit.js.map +1 -0
- package/dist/commands/rebase/living.js +185 -0
- package/dist/commands/rebase/living.js.map +1 -0
- package/dist/commands/rebase/plan.js +245 -0
- package/dist/commands/rebase/plan.js.map +1 -0
- package/dist/commands/rebase/rebase.js +275 -0
- package/dist/commands/rebase/rebase.js.map +1 -0
- package/dist/commands/seed/commit.js +192 -0
- package/dist/commands/seed/commit.js.map +1 -0
- package/dist/commands/seed/plan.js +133 -0
- package/dist/commands/seed/plan.js.map +1 -0
- package/dist/commands/seed/seed.js +172 -0
- package/dist/commands/seed/seed.js.map +1 -0
- package/dist/commands/show/feature.js +178 -0
- package/dist/commands/show/feature.js.map +1 -0
- package/dist/commands/show/marks.js +24 -0
- package/dist/commands/show/marks.js.map +1 -0
- package/dist/commands/{show.js → show/service.js} +63 -152
- package/dist/commands/show/service.js.map +1 -0
- package/dist/commands/show/show.js +77 -0
- package/dist/commands/show/show.js.map +1 -0
- package/dist/commands/status/json.js +45 -0
- package/dist/commands/status/json.js.map +1 -0
- package/dist/commands/status/print.js +157 -0
- package/dist/commands/status/print.js.map +1 -0
- package/dist/commands/status/status.js +119 -0
- package/dist/commands/status/status.js.map +1 -0
- package/dist/commands/steps/steps.js +189 -0
- package/dist/commands/steps/steps.js.map +1 -0
- package/dist/commands/subsystem/edit.js +192 -0
- package/dist/commands/subsystem/edit.js.map +1 -0
- package/dist/commands/subsystem/move.js +265 -0
- package/dist/commands/subsystem/move.js.map +1 -0
- package/dist/commands/subsystem/query.js +115 -0
- package/dist/commands/subsystem/query.js.map +1 -0
- package/dist/commands/subsystem/subsystem.js +101 -0
- package/dist/commands/subsystem/subsystem.js.map +1 -0
- package/dist/commands/subsystem/sync.js +103 -0
- package/dist/commands/subsystem/sync.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +94 -0
- package/dist/commands/subsystem/txn/txn.js.map +1 -0
- package/dist/commands/subsystem/txn/views.js +70 -0
- package/dist/commands/subsystem/txn/views.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +160 -0
- package/dist/commands/unarchive/manifest.js.map +1 -0
- package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
- package/dist/commands/unarchive/restore.js.map +1 -0
- package/dist/commands/unarchive/unarchive.js +67 -0
- package/dist/commands/unarchive/unarchive.js.map +1 -0
- package/dist/commands/validate/arch-coverage.js +204 -0
- package/dist/commands/validate/arch-coverage.js.map +1 -0
- package/dist/commands/validate/checks/fleet-shape.js +296 -0
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
- package/dist/commands/validate/checks/health-deps.js +55 -0
- package/dist/commands/validate/checks/health-deps.js.map +1 -0
- package/dist/commands/validate/checks/requirements.js +254 -0
- package/dist/commands/validate/checks/requirements.js.map +1 -0
- package/dist/commands/validate/checks/sources.js +76 -0
- package/dist/commands/validate/checks/sources.js.map +1 -0
- package/dist/commands/validate/checks/vocabulary.js +31 -0
- package/dist/commands/validate/checks/vocabulary.js.map +1 -0
- package/dist/commands/validate/feature.js +216 -0
- package/dist/commands/validate/feature.js.map +1 -0
- package/dist/commands/validate/fleet/census.js +69 -0
- package/dist/commands/validate/fleet/census.js.map +1 -0
- package/dist/commands/validate/fleet/kind-tags.js +39 -0
- package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
- package/dist/commands/validate/fleet/landscape.js +353 -0
- package/dist/commands/validate/fleet/landscape.js.map +1 -0
- package/dist/commands/validate/fleet/load.js +52 -0
- package/dist/commands/validate/fleet/load.js.map +1 -0
- package/dist/commands/validate/fleet/obligations.js +270 -0
- package/dist/commands/validate/fleet/obligations.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/print.js +63 -0
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
- package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/place.js +13 -0
- package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
- package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/steps.js +197 -0
- package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
- package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
- package/dist/commands/validate/fleet/views/ids.js +40 -0
- package/dist/commands/validate/fleet/views/ids.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +59 -0
- package/dist/commands/validate/fleet/views/stale.js.map +1 -0
- package/dist/commands/validate/links/corpus.js +92 -0
- package/dist/commands/validate/links/corpus.js.map +1 -0
- package/dist/commands/validate/links/glossary.js +75 -0
- package/dist/commands/validate/links/glossary.js.map +1 -0
- package/dist/commands/validate/report.js +269 -0
- package/dist/commands/validate/report.js.map +1 -0
- package/dist/commands/validate/service/api.js +338 -0
- package/dist/commands/validate/service/api.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +219 -0
- package/dist/commands/validate/service/events/events.js.map +1 -0
- package/dist/commands/validate/service/events/message-producers.js +77 -0
- package/dist/commands/validate/service/events/message-producers.js.map +1 -0
- package/dist/commands/validate/service/evidence-pins.js +131 -0
- package/dist/commands/validate/service/evidence-pins.js.map +1 -0
- package/dist/commands/validate/service/service.js +251 -0
- package/dist/commands/validate/service/service.js.map +1 -0
- package/dist/commands/validate/service/specs.js +251 -0
- package/dist/commands/validate/service/specs.js.map +1 -0
- package/dist/commands/validate/service/spine.js +113 -0
- package/dist/commands/validate/service/spine.js.map +1 -0
- package/dist/commands/validate/validate.js +338 -0
- package/dist/commands/validate/validate.js.map +1 -0
- package/dist/commands/verify/cross/diff.js +182 -0
- package/dist/commands/verify/cross/diff.js.map +1 -0
- package/dist/commands/verify/evidence/contract.js +32 -0
- package/dist/commands/verify/evidence/contract.js.map +1 -0
- package/dist/commands/verify/evidence/pins.js +52 -0
- package/dist/commands/verify/evidence/pins.js.map +1 -0
- package/dist/commands/verify/evidence/read.js +72 -0
- package/dist/commands/verify/evidence/read.js.map +1 -0
- package/dist/commands/verify/frozen.js +241 -0
- package/dist/commands/verify/frozen.js.map +1 -0
- package/dist/commands/verify/record.js +226 -0
- package/dist/commands/verify/record.js.map +1 -0
- package/dist/commands/verify/report.js +223 -0
- package/dist/commands/verify/report.js.map +1 -0
- package/dist/commands/verify/results.js +246 -0
- package/dist/commands/verify/results.js.map +1 -0
- package/dist/commands/verify/verify.js +217 -0
- package/dist/commands/verify/verify.js.map +1 -0
- package/dist/commands/vouch/contract.js +11 -0
- package/dist/commands/vouch/contract.js.map +1 -0
- package/dist/commands/vouch/pack/contract.js +2 -0
- package/dist/commands/vouch/pack/contract.js.map +1 -0
- package/dist/commands/vouch/pack/pack.js +266 -0
- package/dist/commands/vouch/pack/pack.js.map +1 -0
- package/dist/commands/vouch/pack/print.js +271 -0
- package/dist/commands/vouch/pack/print.js.map +1 -0
- package/dist/commands/vouch/pack/sections.js +142 -0
- package/dist/commands/vouch/pack/sections.js.map +1 -0
- package/dist/commands/vouch/prompt/confirm.js +46 -0
- package/dist/commands/vouch/prompt/confirm.js.map +1 -0
- package/dist/commands/vouch/report.js +101 -0
- package/dist/commands/vouch/report.js.map +1 -0
- package/dist/commands/vouch/run.js +285 -0
- package/dist/commands/vouch/run.js.map +1 -0
- package/dist/commands/vouch/sample/plan.js +125 -0
- package/dist/commands/vouch/sample/plan.js.map +1 -0
- package/dist/commands/vouch/sample/print.js +109 -0
- package/dist/commands/vouch/sample/print.js.map +1 -0
- package/dist/commands/vouch/vet/verify.js +123 -0
- package/dist/commands/vouch/vet/verify.js.map +1 -0
- package/dist/commands/vouch/vouch.js +176 -0
- package/dist/commands/vouch/vouch.js.map +1 -0
- package/dist/core/agent/agents-md/artifacts.js +220 -0
- package/dist/core/agent/agents-md/artifacts.js.map +1 -0
- package/dist/core/agent/agents-md/command-map.js +324 -0
- package/dist/core/agent/agents-md/command-map.js.map +1 -0
- package/dist/core/agent/agents-md/cycle.js +165 -0
- package/dist/core/agent/agents-md/cycle.js.map +1 -0
- package/dist/core/agent/agents-md/map/explain.js +34 -0
- package/dist/core/agent/agents-md/map/explain.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
- package/dist/core/agent/agents-md/map/mcp.js +41 -0
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
- package/dist/core/agent/agents-md/map/status.js +62 -0
- package/dist/core/agent/agents-md/map/status.js.map +1 -0
- package/dist/core/agent/agents-md/map/subsystem.js +32 -0
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
- package/dist/core/agent/agents-md/map/usecases.js +60 -0
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
- package/dist/core/agent/agents-md/refusals.js +246 -0
- package/dist/core/agent/agents-md/refusals.js.map +1 -0
- package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
- package/dist/core/agent/agents-md/spine.js +40 -0
- package/dist/core/agent/agents-md/spine.js.map +1 -0
- package/dist/core/agent/agents-md.js +170 -0
- package/dist/core/agent/agents-md.js.map +1 -0
- package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
- package/dist/core/agent/agents-stamp.js.map +1 -0
- package/dist/core/agent/contract.js +2 -0
- package/dist/core/agent/contract.js.map +1 -0
- package/dist/core/agent/protocol.js +193 -0
- package/dist/core/agent/protocol.js.map +1 -0
- package/dist/core/agent/scaffold.js +259 -0
- package/dist/core/agent/scaffold.js.map +1 -0
- package/dist/core/agent/support/report.js +103 -0
- package/dist/core/agent/support/report.js.map +1 -0
- package/dist/core/agent/tools/dialects.js +150 -0
- package/dist/core/agent/tools/dialects.js.map +1 -0
- package/dist/core/agent/tools/registry.js +228 -0
- package/dist/core/agent/tools/registry.js.map +1 -0
- package/dist/core/agent/workflows/adopt.js +133 -0
- package/dist/core/agent/workflows/adopt.js.map +1 -0
- package/dist/core/agent/workflows/check.js +337 -0
- package/dist/core/agent/workflows/check.js.map +1 -0
- package/dist/core/agent/workflows/closing.js +262 -0
- package/dist/core/agent/workflows/closing.js.map +1 -0
- package/dist/core/agent/workflows/feature.js +105 -0
- package/dist/core/agent/workflows/feature.js.map +1 -0
- package/dist/core/agent/workflows/implement.js +105 -0
- package/dist/core/agent/workflows/implement.js.map +1 -0
- package/dist/core/agent/workflows/reference/authoring.js +209 -0
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
- package/dist/core/agent/workflows/reference/codes.js +65 -0
- package/dist/core/agent/workflows/reference/codes.js.map +1 -0
- package/dist/core/agent/workflows/reference/done-check.js +188 -0
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
- package/dist/core/agent/workflows/reference/reference.js +16 -0
- package/dist/core/agent/workflows/reference/reference.js.map +1 -0
- package/dist/core/agent/workflows/reference/spine.js +297 -0
- package/dist/core/agent/workflows/reference/spine.js.map +1 -0
- package/dist/core/asyncapi/baseline/plan.js +110 -0
- package/dist/core/asyncapi/baseline/plan.js.map +1 -0
- package/dist/core/asyncapi/baseline/surfaces.js +54 -0
- package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
- package/dist/core/asyncapi/depth.js +164 -0
- package/dist/core/asyncapi/depth.js.map +1 -0
- package/dist/core/asyncapi/digest.js +141 -0
- package/dist/core/asyncapi/digest.js.map +1 -0
- package/dist/core/asyncapi/merge/components.js +91 -0
- package/dist/core/asyncapi/merge/components.js.map +1 -0
- package/dist/core/asyncapi/merge/error.js +33 -0
- package/dist/core/asyncapi/merge/error.js.map +1 -0
- package/dist/core/asyncapi/merge/markers.js +308 -0
- package/dist/core/asyncapi/merge/markers.js.map +1 -0
- package/dist/core/asyncapi/merge/merge.js +215 -0
- package/dist/core/asyncapi/merge/merge.js.map +1 -0
- package/dist/core/asyncapi/merge/pin.js +107 -0
- package/dist/core/asyncapi/merge/pin.js.map +1 -0
- package/dist/core/asyncapi/model.js +11 -0
- package/dist/core/asyncapi/model.js.map +1 -0
- package/dist/core/asyncapi/producers.js +50 -0
- package/dist/core/asyncapi/producers.js.map +1 -0
- package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
- package/dist/core/asyncapi/read.js.map +1 -0
- package/dist/core/brief/brief.js +225 -0
- package/dist/core/brief/brief.js.map +1 -0
- package/dist/core/brief/checks.js +250 -0
- package/dist/core/brief/checks.js.map +1 -0
- package/dist/core/brief/landscape.js +194 -0
- package/dist/core/brief/landscape.js.map +1 -0
- package/dist/core/brief/targets.js +226 -0
- package/dist/core/brief/targets.js.map +1 -0
- package/dist/core/brief/unchecked.js +48 -0
- package/dist/core/brief/unchecked.js.map +1 -0
- package/dist/core/c4/arch.js +174 -0
- package/dist/core/c4/arch.js.map +1 -0
- package/dist/core/c4/likec4.js +90 -0
- package/dist/core/c4/likec4.js.map +1 -0
- package/dist/core/c4/parsed/deployment.js +158 -0
- package/dist/core/c4/parsed/deployment.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js +132 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
- package/dist/core/c4/parsed/specification.js +78 -0
- package/dist/core/c4/parsed/specification.js.map +1 -0
- package/dist/core/c4/parsed/values.js +57 -0
- package/dist/core/c4/parsed/values.js.map +1 -0
- package/dist/core/c4/parsed/view-ids.js +75 -0
- package/dist/core/c4/parsed/view-ids.js.map +1 -0
- package/dist/core/c4/project/architecture.js +45 -0
- package/dist/core/c4/project/architecture.js.map +1 -0
- package/dist/core/c4/project/documents.js +58 -0
- package/dist/core/c4/project/documents.js.map +1 -0
- package/dist/core/c4/project/load.js +187 -0
- package/dist/core/c4/project/load.js.map +1 -0
- package/dist/core/c4/project/staged.js +220 -0
- package/dist/core/c4/project/staged.js.map +1 -0
- package/dist/core/c4/resolve/service.js +159 -0
- package/dist/core/c4/resolve/service.js.map +1 -0
- package/dist/core/c4/resolve/steps.js +144 -0
- package/dist/core/c4/resolve/steps.js.map +1 -0
- package/dist/core/c4/seed/fleet-file.js +201 -0
- package/dist/core/c4/seed/fleet-file.js.map +1 -0
- package/dist/core/c4/seed/items.js +91 -0
- package/dist/core/c4/seed/items.js.map +1 -0
- package/dist/core/c4/seed/stamp.js +48 -0
- package/dist/core/c4/seed/stamp.js.map +1 -0
- package/dist/core/c4/seed/template.js +109 -0
- package/dist/core/c4/seed/template.js.map +1 -0
- package/dist/core/c4/source-mask.js +82 -0
- package/dist/core/c4/source-mask.js.map +1 -0
- package/dist/core/c4/source-scan.js +227 -0
- package/dist/core/c4/source-scan.js.map +1 -0
- package/dist/core/c4/splice/authored-source.js +101 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -0
- package/dist/core/c4/splice/contract.js +10 -0
- package/dist/core/c4/splice/contract.js.map +1 -0
- package/dist/core/c4/splice/delta-blocks.js +139 -0
- package/dist/core/c4/splice/delta-blocks.js.map +1 -0
- package/dist/core/c4/splice/identity/edges.js +36 -0
- package/dist/core/c4/splice/identity/edges.js.map +1 -0
- package/dist/core/c4/splice/landscape-merge.js +269 -0
- package/dist/core/c4/splice/landscape-merge.js.map +1 -0
- package/dist/core/c4/splice/placement.js +194 -0
- package/dist/core/c4/splice/placement.js.map +1 -0
- package/dist/core/c4/workspace.js +174 -0
- package/dist/core/c4/workspace.js.map +1 -0
- package/dist/core/capabilities/authoring/notes.js +141 -0
- package/dist/core/capabilities/authoring/notes.js.map +1 -0
- package/dist/core/capabilities/capabilities.js +124 -0
- package/dist/core/capabilities/capabilities.js.map +1 -0
- package/dist/core/capabilities/delta/doc.js +50 -0
- package/dist/core/capabilities/delta/doc.js.map +1 -0
- package/dist/core/capabilities/delta/overlay.js +129 -0
- package/dist/core/capabilities/delta/overlay.js.map +1 -0
- package/dist/core/capabilities/delta/summary.js +57 -0
- package/dist/core/capabilities/delta/summary.js.map +1 -0
- package/dist/core/capabilities/delta/tree.js +43 -0
- package/dist/core/capabilities/delta/tree.js.map +1 -0
- package/dist/core/capabilities/delta/uncovered.js +222 -0
- package/dist/core/capabilities/delta/uncovered.js.map +1 -0
- package/dist/core/capabilities/findings.js +342 -0
- package/dist/core/capabilities/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/findings.js +181 -0
- package/dist/core/capabilities/realizes/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/join.js +143 -0
- package/dist/core/capabilities/realizes/join.js.map +1 -0
- package/dist/core/capabilities/rollup.js +199 -0
- package/dist/core/capabilities/rollup.js.map +1 -0
- package/dist/core/capabilities/tree.js +126 -0
- package/dist/core/capabilities/tree.js.map +1 -0
- package/dist/core/capabilities/usecase-join.js +277 -0
- package/dist/core/capabilities/usecase-join.js.map +1 -0
- package/dist/core/coherence/authoring/scaffold.js +177 -0
- package/dist/core/coherence/authoring/scaffold.js.map +1 -0
- package/dist/core/coherence/authoring/sentinels.js +74 -0
- package/dist/core/coherence/authoring/sentinels.js.map +1 -0
- package/dist/core/coherence/coherence.js +287 -0
- package/dist/core/coherence/coherence.js.map +1 -0
- package/dist/core/coherence/declared.js +269 -0
- package/dist/core/coherence/declared.js.map +1 -0
- package/dist/core/coherence/events/declared.js +206 -0
- package/dist/core/coherence/events/declared.js.map +1 -0
- package/dist/core/coherence/events/events.js +204 -0
- package/dist/core/coherence/events/events.js.map +1 -0
- package/dist/core/coherence/events/grades.js +166 -0
- package/dist/core/coherence/events/grades.js.map +1 -0
- package/dist/core/coherence/events/lookups.js +122 -0
- package/dist/core/coherence/events/lookups.js.map +1 -0
- package/dist/core/coherence/events/merged.js +40 -0
- package/dist/core/coherence/events/merged.js.map +1 -0
- package/dist/core/coherence/living.js +198 -0
- package/dist/core/coherence/living.js.map +1 -0
- package/dist/core/coherence/lookups.js +155 -0
- package/dist/core/coherence/lookups.js.map +1 -0
- package/dist/core/coherence/pending.js +39 -0
- package/dist/core/coherence/pending.js.map +1 -0
- package/dist/core/conflict-markers.js +60 -0
- package/dist/core/conflict-markers.js.map +1 -0
- package/dist/core/delta/claims.js +73 -0
- package/dist/core/delta/claims.js.map +1 -0
- package/dist/core/delta/delta.js +296 -0
- package/dist/core/delta/delta.js.map +1 -0
- package/dist/core/delta/document.js +177 -0
- package/dist/core/delta/document.js.map +1 -0
- package/dist/core/delta/scope.js +48 -0
- package/dist/core/delta/scope.js.map +1 -0
- package/dist/core/delta/select.js +237 -0
- package/dist/core/delta/select.js.map +1 -0
- package/dist/core/dependencies/dependencies.js +141 -0
- package/dist/core/dependencies/dependencies.js.map +1 -0
- package/dist/core/dependencies/facts.js +165 -0
- package/dist/core/dependencies/facts.js.map +1 -0
- package/dist/core/dependencies/fanin.js +88 -0
- package/dist/core/dependencies/fanin.js.map +1 -0
- package/dist/core/dependencies/graph.js +106 -0
- package/dist/core/dependencies/graph.js.map +1 -0
- package/dist/core/deployment/delta.js +188 -0
- package/dist/core/deployment/delta.js.map +1 -0
- package/dist/core/deployment/objects.js +145 -0
- package/dist/core/deployment/objects.js.map +1 -0
- package/dist/core/diff/base-git.js +148 -0
- package/dist/core/diff/base-git.js.map +1 -0
- package/dist/core/diff/base-state.js +209 -0
- package/dist/core/diff/base-state.js.map +1 -0
- package/dist/core/diff/current-state.js +96 -0
- package/dist/core/diff/current-state.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +188 -0
- package/dist/core/diff/scope/changed-paths.js.map +1 -0
- package/dist/core/diff/semantic.js +243 -0
- package/dist/core/diff/semantic.js.map +1 -0
- package/dist/core/diff/victims.js +247 -0
- package/dist/core/diff/victims.js.map +1 -0
- package/dist/core/docs.js +7 -64
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +222 -0
- package/dist/core/doctor/agents.js.map +1 -0
- package/dist/core/doctor/config.js +135 -0
- package/dist/core/doctor/config.js.map +1 -0
- package/dist/core/doctor/doctor.js +275 -0
- package/dist/core/doctor/doctor.js.map +1 -0
- package/dist/core/doctor/report.js +13 -0
- package/dist/core/doctor/report.js.map +1 -0
- package/dist/core/doctor/residue.js +110 -0
- package/dist/core/doctor/residue.js.map +1 -0
- package/dist/core/document/apply.js +77 -0
- package/dist/core/document/apply.js.map +1 -0
- package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
- package/dist/core/document/frontmatter.js.map +1 -0
- package/dist/core/document/grammar/directives.js +143 -0
- package/dist/core/document/grammar/directives.js.map +1 -0
- package/dist/core/document/parse.js +263 -0
- package/dist/core/document/parse.js.map +1 -0
- package/dist/core/document/scenarios.js +221 -0
- package/dist/core/document/scenarios.js.map +1 -0
- package/dist/core/document/spec.js +241 -0
- package/dist/core/document/spec.js.map +1 -0
- package/dist/core/{config.js → envelope/config.js} +79 -18
- package/dist/core/envelope/config.js.map +1 -0
- package/dist/core/envelope/json.js +175 -0
- package/dist/core/envelope/json.js.map +1 -0
- package/dist/core/{version.js → envelope/version.js} +3 -3
- package/dist/core/envelope/version.js.map +1 -0
- package/dist/core/explain/families.js +175 -0
- package/dist/core/explain/families.js.map +1 -0
- package/dist/core/explain/fix-tables.js +178 -0
- package/dist/core/explain/fix-tables.js.map +1 -0
- package/dist/core/explain/lookup.js +181 -0
- package/dist/core/explain/lookup.js.map +1 -0
- package/dist/core/explain/refusals.js +49 -0
- package/dist/core/explain/refusals.js.map +1 -0
- package/dist/core/explain/terms.js +144 -0
- package/dist/core/explain/terms.js.map +1 -0
- package/dist/core/explore/describe.js +105 -0
- package/dist/core/explore/describe.js.map +1 -0
- package/dist/core/{explore.js → explore/explore.js} +50 -101
- package/dist/core/explore/explore.js.map +1 -0
- package/dist/core/fleet-context.js +120 -78
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +201 -0
- package/dist/core/gate/checks.js.map +1 -0
- package/dist/core/gate/partners.js +205 -0
- package/dist/core/gate/partners.js.map +1 -0
- package/dist/core/gate/report.js +47 -0
- package/dist/core/gate/report.js.map +1 -0
- package/dist/core/gate/verification.js +113 -0
- package/dist/core/gate/verification.js.map +1 -0
- package/dist/core/gherkin/digest.js +60 -0
- package/dist/core/gherkin/digest.js.map +1 -0
- package/dist/core/gherkin/emit.js +177 -0
- package/dist/core/gherkin/emit.js.map +1 -0
- package/dist/core/gherkin/read.js +76 -0
- package/dist/core/gherkin/read.js.map +1 -0
- package/dist/core/gherkin/stale.js +234 -0
- package/dist/core/gherkin/stale.js.map +1 -0
- package/dist/core/gherkin/stamp.js +77 -0
- package/dist/core/gherkin/stamp.js.map +1 -0
- package/dist/core/gherkin/steps/catalogue.js +154 -0
- package/dist/core/gherkin/steps/catalogue.js.map +1 -0
- package/dist/core/gherkin/steps/inventory.js +98 -0
- package/dist/core/gherkin/steps/inventory.js.map +1 -0
- package/dist/core/gherkin/steps/phrase.js +72 -0
- package/dist/core/gherkin/steps/phrase.js.map +1 -0
- package/dist/core/glossary/delta.js +106 -0
- package/dist/core/glossary/delta.js.map +1 -0
- package/dist/core/glossary/tree.js +86 -0
- package/dist/core/glossary/tree.js.map +1 -0
- package/dist/core/kernel/concurrency.js.map +1 -0
- package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
- package/dist/core/kernel/document-bytes.js.map +1 -0
- package/dist/core/kernel/fences.js +42 -0
- package/dist/core/kernel/fences.js.map +1 -0
- package/dist/core/kernel/ids/capability.js +72 -0
- package/dist/core/kernel/ids/capability.js.map +1 -0
- package/dist/core/kernel/ids/dirs.js +33 -0
- package/dist/core/kernel/ids/dirs.js.map +1 -0
- package/dist/core/kernel/ids/feature.js +75 -0
- package/dist/core/kernel/ids/feature.js.map +1 -0
- package/dist/core/kernel/ids/service.js +156 -0
- package/dist/core/kernel/ids/service.js.map +1 -0
- package/dist/core/kernel/ids/subsystem.js +63 -0
- package/dist/core/kernel/ids/subsystem.js.map +1 -0
- package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
- package/dist/core/kernel/path-safety.js.map +1 -0
- package/dist/core/kernel/records.js.map +1 -0
- package/dist/core/links/backlinks.js +68 -0
- package/dist/core/links/backlinks.js.map +1 -0
- package/dist/core/links/case.js +74 -0
- package/dist/core/links/case.js.map +1 -0
- package/dist/core/links/corpus.js +104 -0
- package/dist/core/links/corpus.js.map +1 -0
- package/dist/core/links/findings.js +110 -0
- package/dist/core/links/findings.js.map +1 -0
- package/dist/core/links/parse.js +236 -0
- package/dist/core/links/parse.js.map +1 -0
- package/dist/core/mcp/argv.js +111 -0
- package/dist/core/mcp/argv.js.map +1 -0
- package/dist/core/mcp/author-tools.js +72 -0
- package/dist/core/mcp/author-tools.js.map +1 -0
- package/dist/core/mcp/framing.js +46 -0
- package/dist/core/mcp/framing.js.map +1 -0
- package/dist/core/mcp/protocol.js +280 -0
- package/dist/core/mcp/protocol.js.map +1 -0
- package/dist/core/mcp/tools.js +314 -0
- package/dist/core/mcp/tools.js.map +1 -0
- package/dist/core/obligations/obligations.js +106 -0
- package/dist/core/obligations/obligations.js.map +1 -0
- package/dist/core/openapi/baseline/gate.js +166 -0
- package/dist/core/openapi/baseline/gate.js.map +1 -0
- package/dist/core/openapi/baseline/plan.js +112 -0
- package/dist/core/openapi/baseline/plan.js.map +1 -0
- package/dist/core/openapi/baseline/record.js +175 -0
- package/dist/core/openapi/baseline/record.js.map +1 -0
- package/dist/core/openapi/depth.js +122 -0
- package/dist/core/openapi/depth.js.map +1 -0
- package/dist/core/openapi/digest.js +189 -0
- package/dist/core/openapi/digest.js.map +1 -0
- package/dist/core/{openapi.js → openapi/doc.js} +44 -106
- package/dist/core/openapi/doc.js.map +1 -0
- package/dist/core/openapi/merge/components.js +234 -0
- package/dist/core/openapi/merge/components.js.map +1 -0
- package/dist/core/openapi/merge/error.js +26 -0
- package/dist/core/openapi/merge/error.js.map +1 -0
- package/dist/core/openapi/merge/markers.js +208 -0
- package/dist/core/openapi/merge/markers.js.map +1 -0
- package/dist/core/openapi/merge/merge.js +256 -0
- package/dist/core/openapi/merge/merge.js.map +1 -0
- package/dist/core/openapi/merge/pin.js +128 -0
- package/dist/core/openapi/merge/pin.js.map +1 -0
- package/dist/core/openspec/decide/capabilities.js +244 -0
- package/dist/core/openspec/decide/capabilities.js.map +1 -0
- package/dist/core/openspec/decide/dispositions.js +63 -0
- package/dist/core/openspec/decide/dispositions.js.map +1 -0
- package/dist/core/openspec/decide/renames.js +179 -0
- package/dist/core/openspec/decide/renames.js.map +1 -0
- package/dist/core/openspec/decide/verdict.js +84 -0
- package/dist/core/openspec/decide/verdict.js.map +1 -0
- package/dist/core/openspec/inventory.js +221 -0
- package/dist/core/openspec/inventory.js.map +1 -0
- package/dist/core/openspec/model/mapping.js +76 -0
- package/dist/core/openspec/model/mapping.js.map +1 -0
- package/dist/core/openspec/model/model.js +65 -0
- package/dist/core/openspec/model/model.js.map +1 -0
- package/dist/core/openspec/scan/changes.js +235 -0
- package/dist/core/openspec/scan/changes.js.map +1 -0
- package/dist/core/openspec/scan/config.js +200 -0
- package/dist/core/openspec/scan/config.js.map +1 -0
- package/dist/core/openspec/scan/shape.js +260 -0
- package/dist/core/openspec/scan/shape.js.map +1 -0
- package/dist/core/openspec/scan/walk.js +133 -0
- package/dist/core/openspec/scan/walk.js.map +1 -0
- package/dist/core/openspec/scan/workspace.js +71 -0
- package/dist/core/openspec/scan/workspace.js.map +1 -0
- package/dist/core/owners/codeowners.js +141 -0
- package/dist/core/owners/codeowners.js.map +1 -0
- package/dist/core/pack/features.js +120 -0
- package/dist/core/pack/features.js.map +1 -0
- package/dist/core/pack/joins.js +112 -0
- package/dist/core/pack/joins.js.map +1 -0
- package/dist/core/pack/living.js +137 -0
- package/dist/core/pack/living.js.map +1 -0
- package/dist/core/pack/pack.js +76 -0
- package/dist/core/pack/pack.js.map +1 -0
- package/dist/core/permissions/permissions.js +87 -0
- package/dist/core/permissions/permissions.js.map +1 -0
- package/dist/core/projection/api.js +84 -0
- package/dist/core/projection/api.js.map +1 -0
- package/dist/core/projection/arch-slice.js +63 -0
- package/dist/core/projection/arch-slice.js.map +1 -0
- package/dist/core/projection/events.js +34 -0
- package/dist/core/projection/events.js.map +1 -0
- package/dist/core/provenance/findings.js +208 -0
- package/dist/core/provenance/findings.js.map +1 -0
- package/dist/core/provenance/git.js +225 -0
- package/dist/core/provenance/git.js.map +1 -0
- package/dist/core/provenance/gitq/moves.js +182 -0
- package/dist/core/provenance/gitq/moves.js.map +1 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
- package/dist/core/provenance/sample/scope.js +238 -0
- package/dist/core/provenance/sample/scope.js.map +1 -0
- package/dist/core/provenance/sample/sections.js +61 -0
- package/dist/core/provenance/sample/sections.js.map +1 -0
- package/dist/core/provenance/sources.js +263 -0
- package/dist/core/provenance/sources.js.map +1 -0
- package/dist/core/provenance/stamp.js +232 -0
- package/dist/core/provenance/stamp.js.map +1 -0
- package/dist/core/provenance/walk.js +157 -0
- package/dist/core/provenance/walk.js.map +1 -0
- package/dist/core/repo/authored/paths.js +210 -0
- package/dist/core/repo/authored/paths.js.map +1 -0
- package/dist/core/repo/entries.js +126 -0
- package/dist/core/repo/entries.js.map +1 -0
- package/dist/core/repo/paths.js +277 -0
- package/dist/core/repo/paths.js.map +1 -0
- package/dist/core/repo/repo.js +248 -0
- package/dist/core/repo/repo.js.map +1 -0
- package/dist/core/repo/service-target.js +153 -0
- package/dist/core/repo/service-target.js.map +1 -0
- package/dist/core/repo/state.js +58 -0
- package/dist/core/repo/state.js.map +1 -0
- package/dist/core/repo/tree/find.js +52 -0
- package/dist/core/repo/tree/find.js.map +1 -0
- package/dist/core/repo/tree/fs.js +64 -0
- package/dist/core/repo/tree/fs.js.map +1 -0
- package/dist/core/repo/tree/marker.js +61 -0
- package/dist/core/repo/tree/marker.js.map +1 -0
- package/dist/core/repo/tree/views.js +172 -0
- package/dist/core/repo/tree/views.js.map +1 -0
- package/dist/core/repo/tree/walk.js +234 -0
- package/dist/core/repo/tree/walk.js.map +1 -0
- package/dist/core/results.js +99 -2
- package/dist/core/results.js.map +1 -1
- package/dist/core/scaffold/landscape.js +131 -0
- package/dist/core/scaffold/landscape.js.map +1 -0
- package/dist/core/scaffold/readme.js +84 -0
- package/dist/core/scaffold/readme.js.map +1 -0
- package/dist/core/staging/commit.js +241 -0
- package/dist/core/staging/commit.js.map +1 -0
- package/dist/core/staging/interrupted.js +96 -0
- package/dist/core/staging/interrupted.js.map +1 -0
- package/dist/core/staging/lock.js +206 -0
- package/dist/core/staging/lock.js.map +1 -0
- package/dist/core/staging/recovery/finding.js +35 -0
- package/dist/core/staging/recovery/finding.js.map +1 -0
- package/dist/core/staging/recovery/intent.js +98 -0
- package/dist/core/staging/recovery/intent.js.map +1 -0
- package/dist/core/staging/recovery/recover.js +126 -0
- package/dist/core/staging/recovery/recover.js.map +1 -0
- package/dist/core/staging/recovery/residue.js +62 -0
- package/dist/core/staging/recovery/residue.js.map +1 -0
- package/dist/core/staging/snapshot.js +202 -0
- package/dist/core/staging/snapshot.js.map +1 -0
- package/dist/core/staging/txn/forward.js +171 -0
- package/dist/core/staging/txn/forward.js.map +1 -0
- package/dist/core/staging/txn/journal.js +139 -0
- package/dist/core/staging/txn/journal.js.map +1 -0
- package/dist/core/staging/txn/transaction.js +75 -0
- package/dist/core/staging/txn/transaction.js.map +1 -0
- package/dist/core/staging/writes.js +113 -0
- package/dist/core/staging/writes.js.map +1 -0
- package/dist/core/status/actions/execution.js +49 -0
- package/dist/core/status/actions/execution.js.map +1 -0
- package/dist/core/status/contracts.js +102 -0
- package/dist/core/status/contracts.js.map +1 -0
- package/dist/core/status/feature/artifacts.js +196 -0
- package/dist/core/status/feature/artifacts.js.map +1 -0
- package/dist/core/status/feature/feature.js +207 -0
- package/dist/core/status/feature/feature.js.map +1 -0
- package/dist/core/status/feature/next.js +150 -0
- package/dist/core/status/feature/next.js.map +1 -0
- package/dist/core/status/feature/state.js +21 -0
- package/dist/core/status/feature/state.js.map +1 -0
- package/dist/core/status/fleet/fleet.js +197 -0
- package/dist/core/status/fleet/fleet.js.map +1 -0
- package/dist/core/status/fleet/next.js +207 -0
- package/dist/core/status/fleet/next.js.map +1 -0
- package/dist/core/status/interrupted.js +97 -0
- package/dist/core/status/interrupted.js.map +1 -0
- package/dist/core/status/report.js +31 -0
- package/dist/core/status/report.js.map +1 -0
- package/dist/core/status/scan.js +41 -0
- package/dist/core/status/scan.js.map +1 -0
- package/dist/core/status/verification.js +201 -0
- package/dist/core/status/verification.js.map +1 -0
- package/dist/core/usecases/capability.js +169 -0
- package/dist/core/usecases/capability.js.map +1 -0
- package/dist/core/usecases/delta/claims.js +116 -0
- package/dist/core/usecases/delta/claims.js.map +1 -0
- package/dist/core/usecases/delta/flows.js +103 -0
- package/dist/core/usecases/delta/flows.js.map +1 -0
- package/dist/core/usecases/delta/overlay.js +72 -0
- package/dist/core/usecases/delta/overlay.js.map +1 -0
- package/dist/core/usecases/fleet.js +137 -0
- package/dist/core/usecases/fleet.js.map +1 -0
- package/dist/core/usecases/operations.js +111 -0
- package/dist/core/usecases/operations.js.map +1 -0
- package/dist/core/usecases/place.js +44 -0
- package/dist/core/usecases/place.js.map +1 -0
- package/dist/core/usecases/touch.js +101 -0
- package/dist/core/usecases/touch.js.map +1 -0
- package/dist/core/verify/answers.js +173 -0
- package/dist/core/verify/answers.js.map +1 -0
- package/dist/core/verify/build.js +150 -0
- package/dist/core/verify/build.js.map +1 -0
- package/dist/core/verify/checklist.js +201 -0
- package/dist/core/verify/checklist.js.map +1 -0
- package/dist/core/verify/claims/identity.js +61 -0
- package/dist/core/verify/claims/identity.js.map +1 -0
- package/dist/core/verify/cross/diff.js +97 -0
- package/dist/core/verify/cross/diff.js.map +1 -0
- package/dist/core/verify/evidence/contract.js +169 -0
- package/dist/core/verify/evidence/contract.js.map +1 -0
- package/dist/core/verify/file.js +257 -0
- package/dist/core/verify/file.js.map +1 -0
- package/dist/core/verify/pins/lint.js +157 -0
- package/dist/core/verify/pins/lint.js.map +1 -0
- package/dist/core/verify/pins/pin.js +95 -0
- package/dist/core/verify/pins/pin.js.map +1 -0
- package/dist/core/verify/record.js +142 -0
- package/dist/core/verify/record.js.map +1 -0
- package/dist/core/verify/store/commit.js +89 -0
- package/dist/core/verify/store/commit.js.map +1 -0
- package/dist/core/verify/store/render.js +57 -0
- package/dist/core/verify/store/render.js.map +1 -0
- package/dist/core/vocabulary/health.js +91 -0
- package/dist/core/vocabulary/health.js.map +1 -0
- package/dist/core/vocabulary/issue.js +58 -0
- package/dist/core/vocabulary/issue.js.map +1 -0
- package/dist/core/vocabulary/maturity.js +145 -0
- package/dist/core/vocabulary/maturity.js.map +1 -0
- package/dist/core/{report.js → vocabulary/report.js} +30 -2
- package/dist/core/vocabulary/report.js.map +1 -0
- package/dist/core/vocabulary/steps.js +266 -0
- package/dist/core/vocabulary/steps.js.map +1 -0
- package/dist/core/workspace/discover.js +239 -0
- package/dist/core/workspace/discover.js.map +1 -0
- package/dist/core/workspace/render.js +52 -0
- package/dist/core/workspace/render.js.map +1 -0
- package/examples/README.md +189 -0
- package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
- package/examples/docs/architecture/capabilities.yaml +31 -0
- package/examples/docs/architecture/landscape.likec4 +280 -0
- package/examples/docs/architecture/obligations.yaml +23 -0
- package/examples/docs/architecture/permissions.yaml +50 -0
- package/examples/docs/architecture/subsystems.likec4 +11 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
- package/examples/docs/capabilities/checkout/spec.md +55 -0
- package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
- package/examples/docs/capabilities/order-notifications/spec.md +43 -0
- package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
- package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
- package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
- package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
- package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
- package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
- package/examples/docs/glossary/order.md +15 -0
- package/examples/docs/glossary/payments/authorization.md +14 -0
- package/examples/docs/likec4.config.json +9 -0
- package/examples/docs/loam.json +3 -0
- package/examples/docs/services/checkout-web/model.likec4 +109 -0
- package/examples/docs/services/checkout-web/spec.md +91 -0
- package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
- package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
- package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
- package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
- package/examples/docs/services/order-service/arch.spec.md +127 -0
- package/examples/docs/services/order-service/asyncapi.yaml +108 -0
- package/examples/docs/services/order-service/health.yaml +57 -0
- package/examples/docs/services/order-service/model.likec4 +138 -0
- package/examples/docs/services/order-service/openapi.yaml +257 -0
- package/examples/docs/services/order-service/runbook.md +62 -0
- package/examples/docs/services/order-service/spec.md +134 -0
- package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
- package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
- package/examples/docs/services/payment-service/arch.spec.md +73 -0
- package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
- package/examples/docs/services/payment-service/health.yaml +39 -0
- package/examples/docs/services/payment-service/model.likec4 +67 -0
- package/examples/docs/services/payment-service/openapi.yaml +151 -0
- package/examples/docs/services/payment-service/runbook.md +37 -0
- package/examples/docs/services/payment-service/spec.md +88 -0
- package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
- package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
- package/examples/docs/services/platform/identity-service/health.yaml +70 -0
- package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
- package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
- package/examples/docs/services/platform/identity-service/runbook.md +95 -0
- package/examples/docs/services/platform/identity-service/spec.md +119 -0
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
- package/examples/docs/services/platform/notification-service/health.yaml +35 -0
- package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
- package/examples/docs/services/platform/notification-service/runbook.md +48 -0
- package/examples/docs/services/platform/notification-service/spec.md +112 -0
- package/examples/docs/services/platform/subsystem.yaml +2 -0
- package/examples/fleet.yaml +57 -0
- package/package.json +19 -6
- package/dist/commands/adopt.js +0 -240
- package/dist/commands/adopt.js.map +0 -1
- package/dist/commands/archive.js +0 -1313
- package/dist/commands/archive.js.map +0 -1
- package/dist/commands/delta.js +0 -388
- package/dist/commands/delta.js.map +0 -1
- package/dist/commands/docs-repo-gate.js.map +0 -1
- package/dist/commands/format.js +0 -17
- package/dist/commands/format.js.map +0 -1
- package/dist/commands/gherkin.js +0 -309
- package/dist/commands/gherkin.js.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/list.js +0 -295
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/migrate-openspec.js +0 -950
- package/dist/commands/migrate-openspec.js.map +0 -1
- package/dist/commands/new.js +0 -413
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/rebase.js +0 -356
- package/dist/commands/rebase.js.map +0 -1
- package/dist/commands/show.js.map +0 -1
- package/dist/commands/status.js +0 -262
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/unarchive.js.map +0 -1
- package/dist/commands/validate.js +0 -1773
- package/dist/commands/validate.js.map +0 -1
- package/dist/commands/verify.js +0 -775
- package/dist/commands/verify.js.map +0 -1
- package/dist/commands/vouch.js +0 -421
- package/dist/commands/vouch.js.map +0 -1
- package/dist/core/agent.js +0 -2173
- package/dist/core/agent.js.map +0 -1
- package/dist/core/agents-stamp.js.map +0 -1
- package/dist/core/arch.js +0 -130
- package/dist/core/arch.js.map +0 -1
- package/dist/core/asyncapi.js.map +0 -1
- package/dist/core/brief.js +0 -755
- package/dist/core/brief.js.map +0 -1
- package/dist/core/coherence.js +0 -577
- package/dist/core/coherence.js.map +0 -1
- package/dist/core/concurrency.js.map +0 -1
- package/dist/core/config.js.map +0 -1
- package/dist/core/delta.js +0 -476
- package/dist/core/delta.js.map +0 -1
- package/dist/core/dependencies.js +0 -356
- package/dist/core/dependencies.js.map +0 -1
- package/dist/core/doctor.js +0 -596
- package/dist/core/doctor.js.map +0 -1
- package/dist/core/document-bytes.js.map +0 -1
- package/dist/core/explore.js.map +0 -1
- package/dist/core/frontmatter.js.map +0 -1
- package/dist/core/gherkin.js +0 -463
- package/dist/core/gherkin.js.map +0 -1
- package/dist/core/health.js +0 -67
- package/dist/core/health.js.map +0 -1
- package/dist/core/ids.js +0 -122
- package/dist/core/ids.js.map +0 -1
- package/dist/core/issue.js +0 -9
- package/dist/core/issue.js.map +0 -1
- package/dist/core/json.js +0 -74
- package/dist/core/json.js.map +0 -1
- package/dist/core/likec4.js +0 -489
- package/dist/core/likec4.js.map +0 -1
- package/dist/core/maturity.js +0 -66
- package/dist/core/maturity.js.map +0 -1
- package/dist/core/openapi-merge.js +0 -552
- package/dist/core/openapi-merge.js.map +0 -1
- package/dist/core/openapi.js.map +0 -1
- package/dist/core/openspec-inventory.js +0 -1440
- package/dist/core/openspec-inventory.js.map +0 -1
- package/dist/core/path-safety.js.map +0 -1
- package/dist/core/provenance.js +0 -787
- package/dist/core/provenance.js.map +0 -1
- package/dist/core/records.js.map +0 -1
- package/dist/core/repo.js +0 -438
- package/dist/core/repo.js.map +0 -1
- package/dist/core/report.js.map +0 -1
- package/dist/core/spec.js +0 -505
- package/dist/core/spec.js.map +0 -1
- package/dist/core/staging.js +0 -890
- package/dist/core/staging.js.map +0 -1
- package/dist/core/status.js +0 -1132
- package/dist/core/status.js.map +0 -1
- package/dist/core/steps.js +0 -72
- package/dist/core/steps.js.map +0 -1
- package/dist/core/verify.js +0 -807
- package/dist/core/verify.js.map +0 -1
- package/dist/core/version.js.map +0 -1
- /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
- /package/dist/core/{records.js → kernel/records.js} +0 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
export const LOAM_DONE_CHECK = {
|
|
2
|
+
name: "loam-done-check",
|
|
3
|
+
description: "Reference: the done-check — how `loam verify` derives claims, and verified versus attested",
|
|
4
|
+
// No arguments: one document, printed whole. See ./spine.ts for why the hint
|
|
5
|
+
// is empty rather than a spelled-out "<none>".
|
|
6
|
+
argumentHint: "",
|
|
7
|
+
purpose: "What `loam verify` asks, which of the three channels may answer each kind of claim, how a cross-service feature is recorded from each service's own repository, and why an agent's word yields `attested` rather than `verified`.",
|
|
8
|
+
invocation: "loam instructions loam-done-check",
|
|
9
|
+
placeholders: [],
|
|
10
|
+
spine: ["The done-check"],
|
|
11
|
+
body: `The done-check: what \`loam verify\` asks, who may answer it, and what the answer is worth.
|
|
12
|
+
|
|
13
|
+
## The done-check
|
|
14
|
+
|
|
15
|
+
\`loam verify <FEAT>\` derives a checklist from the feature's own artifacts —
|
|
16
|
+
one claim per new service, per operation the delta adds, per tagged edge that
|
|
17
|
+
names an operation, and per scenario of every changed requirement, arch.spec.md
|
|
18
|
+
deltas included (those claims name their file, and the test they ask for is an
|
|
19
|
+
integration/ops test, not an acceptance test). Each claim has a stable id, so
|
|
20
|
+
two runs are diffable and an answer cannot drift onto a different question.
|
|
21
|
+
|
|
22
|
+
Three answer channels, and they never overlap. The \`scenario.tested\` claims
|
|
23
|
+
are the TEST RUNNER's to answer: run the generated suite and pass its report
|
|
24
|
+
back — cucumber JSON (\`cucumber-js --format json:report.json\`), or loam's own
|
|
25
|
+
runner-neutral shape \`{"loamScenarioReport": 1, "results": [{"digest": "…",
|
|
26
|
+
"status": "passed"}]}\` that any runner can be adapted into, chosen by its
|
|
27
|
+
marker key. The digest is the contract, never the dialect: both read
|
|
28
|
+
\`answered_by: runner\` because both are a real run reporting that digest green,
|
|
29
|
+
so a JUnit or pytest fleet is not held at \`attested\` by a file format —
|
|
30
|
+
\`loam verify <FEAT> --results report.json\`. A claim is confirmed only when a
|
|
31
|
+
report scenario carrying its \`@loam-digest-<16hex>\` tag ran at least one step
|
|
32
|
+
and every step passed — every occurrence, when the report holds a re-run, and
|
|
33
|
+
every before/after hook with it. A failed, undefined, pending or skipped step,
|
|
34
|
+
a failed hook, or no matching scenario at all, is \`unconfirmed\` with the
|
|
35
|
+
reason. The digest is the only identity: a reworded spec scenario matches
|
|
36
|
+
nothing until the suite is regenerated and re-run. When a report exists,
|
|
37
|
+
ALWAYS pass \`--results\` — under it the runner owns every scenario claim, and
|
|
38
|
+
an answers-file entry for one is refused.
|
|
39
|
+
|
|
40
|
+
The \`api.exposes\` claims have a mechanical channel of their own: an API
|
|
41
|
+
contract-test run, passed back with
|
|
42
|
+
\`loam verify <FEAT> --contract-results contract.json\`. loam accepts one
|
|
43
|
+
shape — its own documented contract-results JSON,
|
|
44
|
+
\`{"loamContractReport": 1, "results": [{"operationId": "createSplit",
|
|
45
|
+
"status": "passed"}]}\` — emitted from your contract tool's report in one
|
|
46
|
+
transform (Specmatic's own coverage JSON is refused by design: its "covered"
|
|
47
|
+
status means exercised, not passed, so reading it would confirm what a red run
|
|
48
|
+
refuted). An entry whose status is exactly \`passed\` confirms the claim whose
|
|
49
|
+
operationId it names — the record says \`answered_by: external-runner\` and the
|
|
50
|
+
views mark the line \`[contract]\` — while a failed or unknown status and an
|
|
51
|
+
operation the report never exercised leave the claim \`unconfirmed\` with the
|
|
52
|
+
reason; entries naming operations outside the checklist are ignored.
|
|
53
|
+
\`verify.operation-contested\` (warn) is \`verify.digest-contested\`'s twin on
|
|
54
|
+
this axis: an operationId is unique per contract document, not per fleet, so
|
|
55
|
+
when two services on the checklist expose the same one, a report entry — which
|
|
56
|
+
names no service — cannot say whose suite exercised it, those claims stay
|
|
57
|
+
unconfirmed, and each service records its own with \`--service\`. Under the
|
|
58
|
+
flag the contract report OWNS every api.exposes claim in scope, so an
|
|
59
|
+
answers-file entry for one is refused (\`answers-mismatch\`), and the record
|
|
60
|
+
pins the file consumed in \`contractReport\` (path, sha256 of the bytes, mtime,
|
|
61
|
+
operation count) exactly as \`report\` pins the cucumber one. The verdict
|
|
62
|
+
ladder does not move: external evidence never answers a \`scenario.tested\`
|
|
63
|
+
claim, so \`attested\` versus \`verified\` still turns on scenario claims
|
|
64
|
+
alone.
|
|
65
|
+
|
|
66
|
+
\`--record\` without \`--results\` is the fallback for a service with no runnable
|
|
67
|
+
suite yet: there your word answers the scenario claims too. It still works, and
|
|
68
|
+
it costs something you must know about. The record says who answered
|
|
69
|
+
(\`answered_by: agent\`), and that answer changes the VERDICT: a feature whose
|
|
70
|
+
scenario claims rest on an agent's word is **attested**, not verified.
|
|
71
|
+
\`verify --json\` reports \`verdict: "attested"\` with \`verified: false\` and an
|
|
72
|
+
\`attested\` count, \`loam status\` marks it and adds a \`next.verify-attested\` step,
|
|
73
|
+
and the notice rides in \`notices[]\` under \`verify.scenario-attested\` (warn —
|
|
74
|
+
nothing gates on it, because a legacy service with no suite must still be able
|
|
75
|
+
to ship). Answer them mechanically the moment a suite runs:
|
|
76
|
+
\`loam verify <FEAT> --results <report.json>\`.
|
|
77
|
+
|
|
78
|
+
A record that leaves any claim unconfirmed or unanswered carries
|
|
79
|
+
\`verify.claims-open\` (warn) in \`notices[]\` — the one-line "not a clean
|
|
80
|
+
result" summary with all four counts, on the read view and the frozen
|
|
81
|
+
post-archive view alike; it gates nothing, and a feature with no record at all
|
|
82
|
+
never carries it, because not-started is not partial.
|
|
83
|
+
|
|
84
|
+
Federated \`--record\` also stamps \`evidence_pins\` on each agent-confirmed
|
|
85
|
+
citation — the cited file's sha256 at the attested commit, the cited line's
|
|
86
|
+
text, and the literal token the claim asserts (operationId, message name, edge
|
|
87
|
+
op). One notice rides beside the stamp: \`verify.evidence-token-missing\` (warn,
|
|
88
|
+
verdict-neutral, gates nothing) says a cited file at the attested commit does
|
|
89
|
+
not contain the claim's token — read that evidence first, while the answer can
|
|
90
|
+
still be corrected. \`loam validate\`, run later in the service's repo,
|
|
91
|
+
re-checks the pins and reports drift under the \`evidence.*\` findings; nothing
|
|
92
|
+
there moves a verdict either.
|
|
93
|
+
|
|
94
|
+
Two more codes belong to the record itself. \`verify.record-miscounted\` (error):
|
|
95
|
+
a \`verification.yaml\` whose \`summary\` block contradicts its own \`claims[]\` — the
|
|
96
|
+
record is refused as unreadable rather than believed in either half, and
|
|
97
|
+
re-recording is the repair, never editing the counts. \`verify.digest-contested\`
|
|
98
|
+
(warn): two or more services word a scenario identically, so they share one
|
|
99
|
+
\`@loam-digest-…\` tag and no single cucumber report can say whose suite ran it —
|
|
100
|
+
those scenario claims are left unconfirmed, and each service records its own
|
|
101
|
+
from its own repository with \`--service\`.
|
|
102
|
+
|
|
103
|
+
A federated record also says WHAT EACH SERVICE ANSWERED AGAINST. Every
|
|
104
|
+
attestation carries the \`checklist\` digest it answered and, when the docs repo
|
|
105
|
+
is a git checkout, the \`docsCommit\` that checklist was derived from — so the
|
|
106
|
+
record pins both sides, the service repo through \`commit\` and the question set
|
|
107
|
+
through these two. \`verify.checklist-forked\` (warn) is what that buys: it names the
|
|
108
|
+
services whose attestation answers a DIFFERENT checklist than the feature now
|
|
109
|
+
asks, which the record-level \`checklist\` field alone could never show — it
|
|
110
|
+
flagged every service or none, and never said which answers went stale. It is
|
|
111
|
+
measured against the present rather than pairwise between attestations, because
|
|
112
|
+
services that all answered a since-rewritten delta agree with each other and are
|
|
113
|
+
every one of them stale. Differing \`docsCommit\` values are NOT a finding:
|
|
114
|
+
recording a week apart is normal for a feature in flight.
|
|
115
|
+
Normal mid-rollout and it gates nothing; re-record the services on the older
|
|
116
|
+
version from their own repositories, or accept the split knowingly.
|
|
117
|
+
Attestations written before the field existed carry no \`checklist\` and are not
|
|
118
|
+
counted as a third version — silence is not disagreement.
|
|
119
|
+
|
|
120
|
+
A consumed report is written down: \`report\` and \`contractReport\` on the
|
|
121
|
+
record (or on that repository's attestation) each carry the path, a sha256 of
|
|
122
|
+
the bytes, the file's mtime and how much it held — tagged scenarios for the
|
|
123
|
+
one, distinct operationIds for the other. That identifies WHICH file answered
|
|
124
|
+
the claims, and — when the file is committed — that it matches the attested
|
|
125
|
+
commit. It does not prove the file came from executing that commit; no digest
|
|
126
|
+
can.
|
|
127
|
+
|
|
128
|
+
Everything else you answer from the code, and \`--record answers.json\` takes
|
|
129
|
+
those back, writing \`features/<FEAT>/verification.yaml\` — it travels into the
|
|
130
|
+
archive with the feature and reads without loam. Alongside the mechanical
|
|
131
|
+
flags, the answers file must answer exactly the claims no report owns: an
|
|
132
|
+
entry for a report-owned claim is refused (\`answers-mismatch\`), and a
|
|
133
|
+
mechanical flag alone is refused while claims outside its kind are
|
|
134
|
+
outstanding. Every recorded verdict says who answered it
|
|
135
|
+
(\`answered_by: runner | external-runner | agent\`), so a reviewer can tell a
|
|
136
|
+
green run from somebody's word.
|
|
137
|
+
|
|
138
|
+
**A cross-service feature is verified once per service, and the form matters.**
|
|
139
|
+
The claims of one feature belong to several services, and each service's code
|
|
140
|
+
lives in its own repository — so the recording form is federated:
|
|
141
|
+
|
|
142
|
+
\`\`\`sh
|
|
143
|
+
# in EACH affected service's own repo, not in the docs repo:
|
|
144
|
+
loam verify FEAT-101 --service payment-service --results report.json --record answers.json
|
|
145
|
+
# with an API contract suite, its report answers the api.exposes claims too:
|
|
146
|
+
loam verify FEAT-101 --service payment-service --results report.json --contract-results contract.json --record answers.json
|
|
147
|
+
\`\`\`
|
|
148
|
+
|
|
149
|
+
\`--service <id>\` narrows the checklist to that service's claims and binds the
|
|
150
|
+
answers to this repository's commit, producing one **attestation** per service
|
|
151
|
+
inside a single \`schema: 2\` \`verification.yaml\`. Re-running it in the next
|
|
152
|
+
repo adds that repo's attestation and leaves the others standing. Omitting
|
|
153
|
+
\`--service\` in a repo whose \`loam.json\` declares one substitutes it, so the
|
|
154
|
+
short form is safe there; a \`--service\` naming a service this repo does not
|
|
155
|
+
declare is refused (\`service-mismatch\`), and \`--service\` from a repo that
|
|
156
|
+
declares none is refused (\`repository-unavailable\`) — an attestation has to be
|
|
157
|
+
bound to the code it is about.
|
|
158
|
+
|
|
159
|
+
⚠ **The legacy all-at-once form \`loam verify <FEAT> --record answers.json\` with
|
|
160
|
+
no \`--service\` writes the whole record from one place.** Over a record that
|
|
161
|
+
already carries other services' attestations it is refused outright
|
|
162
|
+
(\`record-federated\`, naming the attestors) rather than merged or migrated — the
|
|
163
|
+
alternative was one repo silently erasing nine others' evidence. Use it only for
|
|
164
|
+
a single-service feature that has never been recorded federated. A
|
|
165
|
+
\`verification.yaml\` that exists but cannot be read is \`record-unreadable\`: it is
|
|
166
|
+
never overwritten and never reported as "no record".
|
|
167
|
+
|
|
168
|
+
Reading is not writing: \`loam verify <FEAT> --service <id>\` **without**
|
|
169
|
+
\`--record\` is a pure lens on the checklist. A read-only verify checklist needs no
|
|
170
|
+
binding and works from the docs repo, or from anywhere else — the refusals above
|
|
171
|
+
are about WRITING an attestation, and only about that. Every run lists all
|
|
172
|
+
subjects in \`services\`, and a recorded run reports in \`discarded\` every previous
|
|
173
|
+
answer the new record does not carry.
|
|
174
|
+
|
|
175
|
+
It refuses an answer set that does not answer the current checklist: an id that
|
|
176
|
+
is not on it (\`answers-mismatch\`), a claim with no answer (same), or a
|
|
177
|
+
\`confirmed\` with no evidence (\`answers-unevidenced\`). Evidence is \`file:line\`.
|
|
178
|
+
A claim you cannot show is \`unconfirmed\` with a note saying why — that is a
|
|
179
|
+
successful record, and it is more useful to the next reader than a yes.
|
|
180
|
+
|
|
181
|
+
**Nothing gates on it.** \`loam archive\` will ship an unverified feature. That
|
|
182
|
+
is deliberate: coherence is something loam computed from the documents, and a
|
|
183
|
+
verdict is your word about code loam never read. A gate in front of shipping
|
|
184
|
+
would only teach everyone that the cheapest way past it is to say yes. The
|
|
185
|
+
record is for the reviewer who comes later, so leave it true.
|
|
186
|
+
`,
|
|
187
|
+
};
|
|
188
|
+
//# sourceMappingURL=done-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"done-check.js","sourceRoot":"","sources":["../../../../../src/core/agent/workflows/reference/done-check.ts"],"names":[],"mappings":"AAcA,MAAM,CAAC,MAAM,eAAe,GAAmB;IAC7C,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,4FAA4F;IAC9F,6EAA6E;IAC7E,+CAA+C;IAC/C,YAAY,EAAE,EAAE;IAChB,OAAO,EACL,mOAAmO;IACrO,UAAU,EAAE,mCAAmC;IAC/C,YAAY,EAAE,EAAE;IAChB,KAAK,EAAE,CAAC,gBAAgB,CAAC;IACzB,IAAI,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+KP;CACA,CAAC","sourcesContent":["/**\n * The `loam-done-check` reference page: how `loam verify` derives its claims,\n * who may answer them, and what separates **verified** from **attested**.\n *\n * It is the longest single section the corpus had after the code inventory —\n * three answer channels, the federated recording form, the evidence pins and\n * the six codes the record itself can carry — and every word of it is read at\n * one moment in the cycle, step 7. AGENTS.md's own step 7 names this page.\n *\n * The text is the AGENTS.md section verbatim. See ./reference.ts for why the\n * four pages exist and what was traded for them.\n */\nimport type { CommandContent } from \"../../contract.js\";\n\nexport const LOAM_DONE_CHECK: CommandContent = {\n name: \"loam-done-check\",\n description:\n \"Reference: the done-check — how `loam verify` derives claims, and verified versus attested\",\n // No arguments: one document, printed whole. See ./spine.ts for why the hint\n // is empty rather than a spelled-out \"<none>\".\n argumentHint: \"\",\n purpose:\n \"What `loam verify` asks, which of the three channels may answer each kind of claim, how a cross-service feature is recorded from each service's own repository, and why an agent's word yields `attested` rather than `verified`.\",\n invocation: \"loam instructions loam-done-check\",\n placeholders: [],\n spine: [\"The done-check\"],\n body: `The done-check: what \\`loam verify\\` asks, who may answer it, and what the answer is worth.\n\n## The done-check\n\n\\`loam verify <FEAT>\\` derives a checklist from the feature's own artifacts —\none claim per new service, per operation the delta adds, per tagged edge that\nnames an operation, and per scenario of every changed requirement, arch.spec.md\ndeltas included (those claims name their file, and the test they ask for is an\nintegration/ops test, not an acceptance test). Each claim has a stable id, so\ntwo runs are diffable and an answer cannot drift onto a different question.\n\nThree answer channels, and they never overlap. The \\`scenario.tested\\` claims\nare the TEST RUNNER's to answer: run the generated suite and pass its report\nback — cucumber JSON (\\`cucumber-js --format json:report.json\\`), or loam's own\nrunner-neutral shape \\`{\"loamScenarioReport\": 1, \"results\": [{\"digest\": \"…\",\n\"status\": \"passed\"}]}\\` that any runner can be adapted into, chosen by its\nmarker key. The digest is the contract, never the dialect: both read\n\\`answered_by: runner\\` because both are a real run reporting that digest green,\nso a JUnit or pytest fleet is not held at \\`attested\\` by a file format —\n\\`loam verify <FEAT> --results report.json\\`. A claim is confirmed only when a\nreport scenario carrying its \\`@loam-digest-<16hex>\\` tag ran at least one step\nand every step passed — every occurrence, when the report holds a re-run, and\nevery before/after hook with it. A failed, undefined, pending or skipped step,\na failed hook, or no matching scenario at all, is \\`unconfirmed\\` with the\nreason. The digest is the only identity: a reworded spec scenario matches\nnothing until the suite is regenerated and re-run. When a report exists,\nALWAYS pass \\`--results\\` — under it the runner owns every scenario claim, and\nan answers-file entry for one is refused.\n\nThe \\`api.exposes\\` claims have a mechanical channel of their own: an API\ncontract-test run, passed back with\n\\`loam verify <FEAT> --contract-results contract.json\\`. loam accepts one\nshape — its own documented contract-results JSON,\n\\`{\"loamContractReport\": 1, \"results\": [{\"operationId\": \"createSplit\",\n\"status\": \"passed\"}]}\\` — emitted from your contract tool's report in one\ntransform (Specmatic's own coverage JSON is refused by design: its \"covered\"\nstatus means exercised, not passed, so reading it would confirm what a red run\nrefuted). An entry whose status is exactly \\`passed\\` confirms the claim whose\noperationId it names — the record says \\`answered_by: external-runner\\` and the\nviews mark the line \\`[contract]\\` — while a failed or unknown status and an\noperation the report never exercised leave the claim \\`unconfirmed\\` with the\nreason; entries naming operations outside the checklist are ignored.\n\\`verify.operation-contested\\` (warn) is \\`verify.digest-contested\\`'s twin on\nthis axis: an operationId is unique per contract document, not per fleet, so\nwhen two services on the checklist expose the same one, a report entry — which\nnames no service — cannot say whose suite exercised it, those claims stay\nunconfirmed, and each service records its own with \\`--service\\`. Under the\nflag the contract report OWNS every api.exposes claim in scope, so an\nanswers-file entry for one is refused (\\`answers-mismatch\\`), and the record\npins the file consumed in \\`contractReport\\` (path, sha256 of the bytes, mtime,\noperation count) exactly as \\`report\\` pins the cucumber one. The verdict\nladder does not move: external evidence never answers a \\`scenario.tested\\`\nclaim, so \\`attested\\` versus \\`verified\\` still turns on scenario claims\nalone.\n\n\\`--record\\` without \\`--results\\` is the fallback for a service with no runnable\nsuite yet: there your word answers the scenario claims too. It still works, and\nit costs something you must know about. The record says who answered\n(\\`answered_by: agent\\`), and that answer changes the VERDICT: a feature whose\nscenario claims rest on an agent's word is **attested**, not verified.\n\\`verify --json\\` reports \\`verdict: \"attested\"\\` with \\`verified: false\\` and an\n\\`attested\\` count, \\`loam status\\` marks it and adds a \\`next.verify-attested\\` step,\nand the notice rides in \\`notices[]\\` under \\`verify.scenario-attested\\` (warn —\nnothing gates on it, because a legacy service with no suite must still be able\nto ship). Answer them mechanically the moment a suite runs:\n\\`loam verify <FEAT> --results <report.json>\\`.\n\nA record that leaves any claim unconfirmed or unanswered carries\n\\`verify.claims-open\\` (warn) in \\`notices[]\\` — the one-line \"not a clean\nresult\" summary with all four counts, on the read view and the frozen\npost-archive view alike; it gates nothing, and a feature with no record at all\nnever carries it, because not-started is not partial.\n\nFederated \\`--record\\` also stamps \\`evidence_pins\\` on each agent-confirmed\ncitation — the cited file's sha256 at the attested commit, the cited line's\ntext, and the literal token the claim asserts (operationId, message name, edge\nop). One notice rides beside the stamp: \\`verify.evidence-token-missing\\` (warn,\nverdict-neutral, gates nothing) says a cited file at the attested commit does\nnot contain the claim's token — read that evidence first, while the answer can\nstill be corrected. \\`loam validate\\`, run later in the service's repo,\nre-checks the pins and reports drift under the \\`evidence.*\\` findings; nothing\nthere moves a verdict either.\n\nTwo more codes belong to the record itself. \\`verify.record-miscounted\\` (error):\na \\`verification.yaml\\` whose \\`summary\\` block contradicts its own \\`claims[]\\` — the\nrecord is refused as unreadable rather than believed in either half, and\nre-recording is the repair, never editing the counts. \\`verify.digest-contested\\`\n(warn): two or more services word a scenario identically, so they share one\n\\`@loam-digest-…\\` tag and no single cucumber report can say whose suite ran it —\nthose scenario claims are left unconfirmed, and each service records its own\nfrom its own repository with \\`--service\\`.\n\nA federated record also says WHAT EACH SERVICE ANSWERED AGAINST. Every\nattestation carries the \\`checklist\\` digest it answered and, when the docs repo\nis a git checkout, the \\`docsCommit\\` that checklist was derived from — so the\nrecord pins both sides, the service repo through \\`commit\\` and the question set\nthrough these two. \\`verify.checklist-forked\\` (warn) is what that buys: it names the\nservices whose attestation answers a DIFFERENT checklist than the feature now\nasks, which the record-level \\`checklist\\` field alone could never show — it\nflagged every service or none, and never said which answers went stale. It is\nmeasured against the present rather than pairwise between attestations, because\nservices that all answered a since-rewritten delta agree with each other and are\nevery one of them stale. Differing \\`docsCommit\\` values are NOT a finding:\nrecording a week apart is normal for a feature in flight.\nNormal mid-rollout and it gates nothing; re-record the services on the older\nversion from their own repositories, or accept the split knowingly.\nAttestations written before the field existed carry no \\`checklist\\` and are not\ncounted as a third version — silence is not disagreement.\n\nA consumed report is written down: \\`report\\` and \\`contractReport\\` on the\nrecord (or on that repository's attestation) each carry the path, a sha256 of\nthe bytes, the file's mtime and how much it held — tagged scenarios for the\none, distinct operationIds for the other. That identifies WHICH file answered\nthe claims, and — when the file is committed — that it matches the attested\ncommit. It does not prove the file came from executing that commit; no digest\ncan.\n\nEverything else you answer from the code, and \\`--record answers.json\\` takes\nthose back, writing \\`features/<FEAT>/verification.yaml\\` — it travels into the\narchive with the feature and reads without loam. Alongside the mechanical\nflags, the answers file must answer exactly the claims no report owns: an\nentry for a report-owned claim is refused (\\`answers-mismatch\\`), and a\nmechanical flag alone is refused while claims outside its kind are\noutstanding. Every recorded verdict says who answered it\n(\\`answered_by: runner | external-runner | agent\\`), so a reviewer can tell a\ngreen run from somebody's word.\n\n**A cross-service feature is verified once per service, and the form matters.**\nThe claims of one feature belong to several services, and each service's code\nlives in its own repository — so the recording form is federated:\n\n\\`\\`\\`sh\n# in EACH affected service's own repo, not in the docs repo:\nloam verify FEAT-101 --service payment-service --results report.json --record answers.json\n# with an API contract suite, its report answers the api.exposes claims too:\nloam verify FEAT-101 --service payment-service --results report.json --contract-results contract.json --record answers.json\n\\`\\`\\`\n\n\\`--service <id>\\` narrows the checklist to that service's claims and binds the\nanswers to this repository's commit, producing one **attestation** per service\ninside a single \\`schema: 2\\` \\`verification.yaml\\`. Re-running it in the next\nrepo adds that repo's attestation and leaves the others standing. Omitting\n\\`--service\\` in a repo whose \\`loam.json\\` declares one substitutes it, so the\nshort form is safe there; a \\`--service\\` naming a service this repo does not\ndeclare is refused (\\`service-mismatch\\`), and \\`--service\\` from a repo that\ndeclares none is refused (\\`repository-unavailable\\`) — an attestation has to be\nbound to the code it is about.\n\n⚠ **The legacy all-at-once form \\`loam verify <FEAT> --record answers.json\\` with\nno \\`--service\\` writes the whole record from one place.** Over a record that\nalready carries other services' attestations it is refused outright\n(\\`record-federated\\`, naming the attestors) rather than merged or migrated — the\nalternative was one repo silently erasing nine others' evidence. Use it only for\na single-service feature that has never been recorded federated. A\n\\`verification.yaml\\` that exists but cannot be read is \\`record-unreadable\\`: it is\nnever overwritten and never reported as \"no record\".\n\nReading is not writing: \\`loam verify <FEAT> --service <id>\\` **without**\n\\`--record\\` is a pure lens on the checklist. A read-only verify checklist needs no\nbinding and works from the docs repo, or from anywhere else — the refusals above\nare about WRITING an attestation, and only about that. Every run lists all\nsubjects in \\`services\\`, and a recorded run reports in \\`discarded\\` every previous\nanswer the new record does not carry.\n\nIt refuses an answer set that does not answer the current checklist: an id that\nis not on it (\\`answers-mismatch\\`), a claim with no answer (same), or a\n\\`confirmed\\` with no evidence (\\`answers-unevidenced\\`). Evidence is \\`file:line\\`.\nA claim you cannot show is \\`unconfirmed\\` with a note saying why — that is a\nsuccessful record, and it is more useful to the next reader than a yes.\n\n**Nothing gates on it.** \\`loam archive\\` will ship an unverified feature. That\nis deliberate: coherence is something loam computed from the documents, and a\nverdict is your word about code loam never read. A gate in front of shipping\nwould only teach everyone that the cheapest way past it is to say yes. The\nrecord is for the reviewer who comes later, so leave it true.\n`,\n};\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { LOAM_AUTHORING } from "./authoring.js";
|
|
2
|
+
import { LOAM_CODES } from "./codes.js";
|
|
3
|
+
import { LOAM_DONE_CHECK } from "./done-check.js";
|
|
4
|
+
import { LOAM_SPINE } from "./spine.js";
|
|
5
|
+
/**
|
|
6
|
+
* The four pages, in the order AGENTS.md's own index names them — which is the
|
|
7
|
+
* order their sections stood in the document they left, so a reader who knew
|
|
8
|
+
* the file finds them where they were.
|
|
9
|
+
*/
|
|
10
|
+
export const REFERENCES = [
|
|
11
|
+
LOAM_CODES,
|
|
12
|
+
LOAM_SPINE,
|
|
13
|
+
LOAM_AUTHORING,
|
|
14
|
+
LOAM_DONE_CHECK,
|
|
15
|
+
];
|
|
16
|
+
//# sourceMappingURL=reference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference.js","sourceRoot":"","sources":["../../../../../src/core/agent/workflows/reference/reference.ts"],"names":[],"mappings":"AAwDA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAqB;IAC1C,UAAU;IACV,UAAU;IACV,cAAc;IACd,eAAe;CAChB,CAAC","sourcesContent":["/**\n * Why the reference pages exist, what decided which content became one, and the\n * list itself.\n *\n * THE MEASUREMENT. AGENTS.md is auto-loaded from the working directory by every\n * agents.md-aware host, on every session, and `loam init` writes it once and\n * never refreshes it. It had reached 109,399 bytes. Codex truncates the\n * AGENTS.md chain at 32,768 bytes and Windsurf caps a workspace rule file at\n * 12,000 characters — both silently, with no error and no way for the reader to\n * tell which half it was holding. Roughly seventy percent of the document was\n * being dropped on those two hosts, and which seventy percent depended on the\n * host rather than on what the reader needed.\n *\n * THE SPLIT RULE, which is not \"move the big parts\". Two kinds of content were\n * mixed in one file:\n *\n * - what a reader needs to FORM a question — the layout, the cycle, which\n * element is which service, what gates and what only advises, what the words\n * mean. Without it an agent cannot ask anything, so it cannot be behind a\n * command it does not know to run. That stays in AGENTS.md;\n * - what a reader CONSULTS at a moment it can name — the grammars for writing\n * one document, the inventory of what a command can report, the done-check's\n * channels. The moment is nameable (\"I am writing an arch.spec.md\", \"a run\n * reported something\"), so the page can be fetched then, and paying for it on\n * every invocation of every session bought nothing.\n *\n * That rule is why `## Which element IS which service` stayed behind in\n * ../../agents-md/spine.ts while the rest of the ID spine left, and why the\n * per-invocation code inventory left while the three facts about the `--json`\n * envelope stayed. Size followed from the rule; it did not drive it.\n *\n * WHAT A PAGE COSTS AND WHAT IT BUYS. A page printed by the binary describes\n * the loam you are about to run, not the one that scaffolded the repository —\n * which is strictly better than a file `loam init` writes once and never\n * refreshes, and is why `agents.stale` exists at all. What it costs is\n * discoverability: a reference nobody can find is content deleted with extra\n * steps. Three things pay that back, and all three are asserted rather than\n * hoped for — AGENTS.md's own \"The reference pages\" section names each page\n * with the exact command (test/agents.test.ts), bare `loam instructions` lists\n * them beside the workflows (test/instructions.test.ts), and every stable code\n * that left is still in the corpus test/codes-drift.test.ts reads, because\n * {@link REFERENCES} feeds `PROTOCOLS`.\n *\n * WHAT A PAGE IS NOT. It is not a workflow. It has no steps, takes no\n * arguments, and `loam init` writes NO file for it — ../../protocol.ts feeds\n * this list to `PROTOCOLS` and `placeholderProblems` and to nothing else, so\n * `SLASH_COMMANDS` and `plannedCommandFiles` never see it and no repository\n * gains a generated artifact. Four pages are four more files in every repo loam\n * touches, and the whole point of the move was to stop shipping bytes nobody\n * asked for.\n *\n * THE MOVE WAS A MOVE. Every page's text is its AGENTS.md section verbatim.\n * Nothing was rewritten on the way, because a paraphrase is a second copy that\n * can drift — and removing one of those is what this whole change is for.\n */\nimport type { CommandContent } from \"../../contract.js\";\nimport { LOAM_AUTHORING } from \"./authoring.js\";\nimport { LOAM_CODES } from \"./codes.js\";\nimport { LOAM_DONE_CHECK } from \"./done-check.js\";\nimport { LOAM_SPINE } from \"./spine.js\";\n\n/**\n * The four pages, in the order AGENTS.md's own index names them — which is the\n * order their sections stood in the document they left, so a reader who knew\n * the file finds them where they were.\n */\nexport const REFERENCES: CommandContent[] = [\n LOAM_CODES,\n LOAM_SPINE,\n LOAM_AUTHORING,\n LOAM_DONE_CHECK,\n];\n"]}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
export const LOAM_SPINE = {
|
|
2
|
+
name: "loam-spine",
|
|
3
|
+
description: "Reference: the ID spine — every join between the artifacts, and the baseline pins",
|
|
4
|
+
// A reference page takes no arguments: it is one document, printed whole.
|
|
5
|
+
// The empty hint is what `loam instructions` prints beside the name, and an
|
|
6
|
+
// invented one ("<none>") would read as an argument somebody could pass.
|
|
7
|
+
argumentHint: "",
|
|
8
|
+
purpose: "The exact joins the artifacts are held together by — operationId, message name, `Covers:`, `Requires:`, `Capability:`/`Realizes:` — and the `Based-On:` pins that stop two features in flight reverting each other.",
|
|
9
|
+
invocation: "loam instructions loam-spine",
|
|
10
|
+
placeholders: [],
|
|
11
|
+
spine: [
|
|
12
|
+
"The ID spine",
|
|
13
|
+
"The requirement baseline — `Based-On:`",
|
|
14
|
+
"Drawing a shared broker",
|
|
15
|
+
"A message produced outside the fleet",
|
|
16
|
+
],
|
|
17
|
+
body: `The ID spine: how the artifacts join, and what pins a change to the version it was written against.
|
|
18
|
+
|
|
19
|
+
## The ID spine
|
|
20
|
+
|
|
21
|
+
The spine is a family of exact joins, not one magic id: feature tag; service
|
|
22
|
+
binding; synchronous operation; asynchronous message; architecture coverage;
|
|
23
|
+
authorization permission. Three artifacts describe the same synchronous call in
|
|
24
|
+
three languages. They are joined by the **operationId**, and that join is what
|
|
25
|
+
\`loam validate\` checks:
|
|
26
|
+
|
|
27
|
+
- architecture — a C4 edge names the operation it calls:
|
|
28
|
+
\`checkoutWeb -> paymentService 'Calls authorizePayment' { metadata { op 'authorizePayment' } }\`
|
|
29
|
+
- behaviour — a requirement declares the operations it governs:
|
|
30
|
+
\`Operations: authorizePayment\`
|
|
31
|
+
- contract — the provider's \`openapi.yaml\` defines it:
|
|
32
|
+
\`operationId: authorizePayment\`
|
|
33
|
+
|
|
34
|
+
Spell it identically in all three places. A mismatch is an error, not a warning:
|
|
35
|
+
an edge calling an operation nobody defines is a broken contract between services.
|
|
36
|
+
|
|
37
|
+
Features are joined the same way: a delta's new elements and edges carry the
|
|
38
|
+
feature id as a LikeC4 tag (\`#FEAT-101\`), and that tag is exactly what
|
|
39
|
+
\`loam archive\` folds into the living landscape. Untagged elements in a delta are
|
|
40
|
+
context for the diagram, not changes.
|
|
41
|
+
|
|
42
|
+
The other joins use the same discipline:
|
|
43
|
+
|
|
44
|
+
- \`metadata { publishes|consumes 'message' }\` on an edge ↔
|
|
45
|
+
\`Publishes:\`/\`Consumes:\` in a requirement ↔ the AsyncAPI 3 message name;
|
|
46
|
+
- \`Covers:\` in an architecture requirement ↔ a C4 element/edge or
|
|
47
|
+
\`alert:<id>\` / \`sli:<id>\` from health.yaml;
|
|
48
|
+
- \`Requires: <subject>/<permission>\` in either requirement file ↔ the fleet
|
|
49
|
+
declaration in \`architecture/permissions.yaml\`;
|
|
50
|
+
- \`Capability:\` in either requirement file ↔ a declared id in
|
|
51
|
+
\`architecture/capabilities.yaml\`. A LIST, comma-separated — the relation is
|
|
52
|
+
many-to-many in both directions.
|
|
53
|
+
|
|
54
|
+
The permissions file is opt-in: a fleet with no \`Requires:\` line owes none.
|
|
55
|
+
Once a requirement names a permission, an undeclared pair is
|
|
56
|
+
\`permissions.unknown\` (error). An unreadable vocabulary is
|
|
57
|
+
\`permissions.invalid\`; a declaration nothing requires is
|
|
58
|
+
\`permissions.unenforced\` (warn). The subject is what the permission is checked
|
|
59
|
+
ON (user, profile, service), not necessarily the caller. \`owned_by\` and
|
|
60
|
+
\`enforced_by\` are explanatory and are not resolved against services; this is
|
|
61
|
+
a checked document join, not proof that the identity provider implements it.
|
|
62
|
+
|
|
63
|
+
The capability axis opts in the OTHER way: the FILES are the opt-in, not the
|
|
64
|
+
line. A fleet with neither \`architecture/capabilities.yaml\` nor a
|
|
65
|
+
\`capabilities/\` directory gets no capability findings at all, however many
|
|
66
|
+
\`Capability:\` lines its requirements already carry.
|
|
67
|
+
|
|
68
|
+
A capability may be declared on either side, and the vocabulary is the UNION.
|
|
69
|
+
A name alone is a line in \`architecture/capabilities.yaml\` —
|
|
70
|
+
\`capabilities: {<id>: {description, owner}}\`, nested ids such as
|
|
71
|
+
\`payments/refunds\` kept as one flat key. A name with prose behind it gets a
|
|
72
|
+
document instead: \`capabilities/<id>/spec.md\`, the AUTHORED business tree,
|
|
73
|
+
one directory per capability with nesting spelled by the tree
|
|
74
|
+
(\`capabilities/payments/refunds/spec.md\`). The directory is the list — there
|
|
75
|
+
is no manifest — and its existence is what opts the fleet in, so \`loam init\`
|
|
76
|
+
does not scaffold it. A directory holding neither the document nor a
|
|
77
|
+
capability beneath it is \`capability.doc-missing\` (warn).
|
|
78
|
+
|
|
79
|
+
The document carries narrative and then \`## Requirements\`, in the same
|
|
80
|
+
grammar every spec.md uses, with two rules of its own. Every requirement needs
|
|
81
|
+
a \`Requirement-ID:\` — these documents outlive the services that realize them,
|
|
82
|
+
so identity is the line and not the heading; without one it is
|
|
83
|
+
\`capability.requirement-unidentified\` (error). And a capability requirement
|
|
84
|
+
must be observable OUTSIDE the fleet: \`Operations:\`, \`Covers:\`,
|
|
85
|
+
\`Publishes:\` and \`Consumes:\` all resolve against one service's own contract,
|
|
86
|
+
so carrying any of them is \`capability.requirement-service-scoped\` (error) —
|
|
87
|
+
write it in that service's spec.md instead. Naming no service is an authoring
|
|
88
|
+
rule that PR review holds, not one loam checks: matching service names in prose
|
|
89
|
+
would be a heuristic, and loam does not guess. The axis's own two joins are
|
|
90
|
+
refused here as well (\`capability.requirement-inert-join\`, error): they point
|
|
91
|
+
INTO the tree, so nothing reads them written inside it.
|
|
92
|
+
|
|
93
|
+
A FEATURE changes a capability document with a delta, not by editing it:
|
|
94
|
+
\`features/<FEAT>/capabilities/<id>/spec.md\`, the same
|
|
95
|
+
\`## ADDED|MODIFIED|REMOVED Requirements\` grammar the service deltas use, the
|
|
96
|
+
same nesting (\`features/<FEAT>/capabilities/payments/refunds/\`), the same
|
|
97
|
+
\`Based-On:\` pins, and the same \`delta.*\` refusals with the capability id as
|
|
98
|
+
the subject. \`scaffold.placeholder\` reads it too, so an unedited template
|
|
99
|
+
heading refuses the archive here as it does in a spec delta, and
|
|
100
|
+
\`capability.doc-missing\` (warn) names a directory in it that holds no
|
|
101
|
+
\`spec.md\` — an empty one is not a delta, so the merge would carry nothing.
|
|
102
|
+
\`loam archive\` merges it into the living document in the same
|
|
103
|
+
transaction as the service merge, creating \`capabilities/<id>/spec.md\` — and
|
|
104
|
+
so the whole tree — when this is the first feature to mention it; the heading
|
|
105
|
+
is the id and the narrative above \`## Requirements\` is yours to write, because
|
|
106
|
+
loam merges requirements and never prose. The document's three rules above are
|
|
107
|
+
graded on the delta BEFORE the merge, so a capability requirement filed at the
|
|
108
|
+
wrong altitude is refused while you still hold it. Editing the living document
|
|
109
|
+
directly is still legal; the delta is what makes two features touching one
|
|
110
|
+
promise collide loudly instead of silently. \`loam new <FEAT> --capability <id>\`
|
|
111
|
+
scaffolds that delta, and \`loam show <FEAT>\` and \`loam delta <FEAT>\` list the
|
|
112
|
+
promises a feature changes — a capability delta names no service, so without
|
|
113
|
+
those two a business-only feature reads as a feature carrying nothing.
|
|
114
|
+
|
|
115
|
+
A service requirement says which promise it serves with \`Realizes:\`, entries
|
|
116
|
+
spelled \`<capability-id>#<Requirement-ID>\`. This is the join the tree exists
|
|
117
|
+
for, and it is NOT the same claim as \`Capability:\` beside it — that names a
|
|
118
|
+
theme, this names one promise, and a requirement commonly carries both. The
|
|
119
|
+
capability half of an entry is what makes the target addressable, since a
|
|
120
|
+
\`Requirement-ID\` is unique only inside its own document; the separator is the
|
|
121
|
+
LAST \`#\`, because the requirement half's grammar excludes one and the
|
|
122
|
+
capability half's does not. Written by whoever implements the requirement,
|
|
123
|
+
never by the analyst — a business document must not be edited every time the
|
|
124
|
+
fleet rearranges which service carries which part.
|
|
125
|
+
|
|
126
|
+
Both directions are graded. An entry naming no capability requirement is
|
|
127
|
+
\`capability.realizes-unknown\` (error; in a feature delta it gates
|
|
128
|
+
\`loam archive\`, \`--approve\`-overridable), and its message says which of the
|
|
129
|
+
five failures happened — malformed entry, undeclared capability, declared but
|
|
130
|
+
undocumented, document with no requirements, or an id that document does not
|
|
131
|
+
declare. A capability requirement nothing keeps is
|
|
132
|
+
\`capability.requirement-unrealized\` (warn, one per requirement): it never
|
|
133
|
+
gates, because writing the business document ahead of the fleet is the intended
|
|
134
|
+
use. \`loam list capabilities --json\` carries the whole join — each capability's
|
|
135
|
+
requirements, and what realizes each one.
|
|
136
|
+
|
|
137
|
+
\`Realizes:\` is not the only way to keep a promise, and for a criterion that
|
|
138
|
+
CROSSES services it is not enough: "I enter a login and a password and I am in"
|
|
139
|
+
belongs to no single service's spec, because each promises only its own part. A
|
|
140
|
+
USE CASE can carry it, because it IS the hop sequence. So a \`dynamic view\`
|
|
141
|
+
already tagged \`#cap-<slug>\` may carry \`#req-<slug>\` as well, naming one of
|
|
142
|
+
that capability's requirements — the architect's answer to the analyst's
|
|
143
|
+
promise, graded by the same four \`usecase.*\` codes down to the operation. The
|
|
144
|
+
second tag is SCOPED by the first: a \`Requirement-ID\` is unique only inside its
|
|
145
|
+
own document, so a \`#req-\` tag on a view that resolves no capability, or two, is
|
|
146
|
+
\`usecase.requirement-unresolved\` (error) rather than a guess. Several \`#req-\`
|
|
147
|
+
tags on one view are legal — a flow commonly keeps two promises. Both tags spell
|
|
148
|
+
their id with every character outside \`[A-Za-z0-9_-]\` flattened to \`-\`,
|
|
149
|
+
because that is all a LikeC4 tag name accepts.
|
|
150
|
+
|
|
151
|
+
Against that vocabulary an undeclared name is \`capability.unknown\` (error; in
|
|
152
|
+
a feature delta it gates \`loam archive\`, \`--approve\`-overridable), an
|
|
153
|
+
unreadable \`capabilities.yaml\` is \`capability.invalid\` exactly once per run
|
|
154
|
+
with every grade that resolves against the vocabulary suspended behind it, and
|
|
155
|
+
a declared capability no living requirement realizes is
|
|
156
|
+
\`capability.unrealized\` (warn, one per capability). The fleet total is
|
|
157
|
+
readable: \`loam list capabilities\` reports each capability's realizing
|
|
158
|
+
requirements, their services and the draft/verified split, and
|
|
159
|
+
\`loam explore --capability <id>\` seeds an exploration from the realizing
|
|
160
|
+
services.
|
|
161
|
+
|
|
162
|
+
## The requirement baseline — \`Based-On:\`
|
|
163
|
+
|
|
164
|
+
A MODIFIED requirement carries its FULL new text, not a diff, so archive does not
|
|
165
|
+
merge your wording into the living one — it REPLACES it. When two features rewrite
|
|
166
|
+
the same requirement, the loser loses everything they wrote, scenarios and all.
|
|
167
|
+
|
|
168
|
+
\`loam validate\` names the other feature while both are in flight
|
|
169
|
+
(\`delta.modified-conflict\`), but that warning cannot see the case that actually
|
|
170
|
+
happens: the first feature archives, stops being an active feature, the second
|
|
171
|
+
revalidates GREEN, and its archive lands text written against a document that no
|
|
172
|
+
longer exists — \`+0 ~1 -0\`, exit 0, nobody told.
|
|
173
|
+
|
|
174
|
+
So a MODIFIED or REMOVED requirement pins the living version it was written
|
|
175
|
+
against, directly under its identity:
|
|
176
|
+
|
|
177
|
+
\`\`\`markdown
|
|
178
|
+
### Requirement: Cancel an order
|
|
179
|
+
Requirement-ID: REQ-042
|
|
180
|
+
Based-On: 3bcc7a40f2b23ff3
|
|
181
|
+
\`\`\`
|
|
182
|
+
|
|
183
|
+
You do not compute that value — \`loam rebase FEAT-101\` writes it, for every
|
|
184
|
+
MODIFIED/REMOVED requirement in the feature, from the living text as it stands
|
|
185
|
+
right now. Run it when you finish authoring a delta, and again after you resolve
|
|
186
|
+
a collision. Archive refuses on a stale pin (\`delta.baseline-stale\`) and says
|
|
187
|
+
both digests.
|
|
188
|
+
|
|
189
|
+
**Restamping is not resolving.** A pin claims "I read this version". If
|
|
190
|
+
\`loam rebase\` reports a requirement as moved, re-read the living requirement and
|
|
191
|
+
fold in what you still mean BEFORE you ship — otherwise you have re-pinned to a
|
|
192
|
+
document you never read, and archive will merge your text over theirs with
|
|
193
|
+
loam's blessing. There is no automatic three-way merge and there will not be one:
|
|
194
|
+
requirement prose does not merge, people do.
|
|
195
|
+
|
|
196
|
+
A delta adopted from OpenSpec has no such line and gets a warning
|
|
197
|
+
(\`delta.baseline-missing\`) — one that GATES the archive: unpinned is exactly
|
|
198
|
+
the shape that reverts someone else's landed change at exit 0. A migrated
|
|
199
|
+
corpus is not stranded by this: \`loam rebase <FEAT>\` pins every delta in one
|
|
200
|
+
command, and a human's \`--approve\` archives unpinned when the loss is meant.
|
|
201
|
+
|
|
202
|
+
### The same pin on the contract axis — and why it matters more there
|
|
203
|
+
|
|
204
|
+
A feature's \`openapi.yaml\` is a COMPLETE document, not a patch: you restate the
|
|
205
|
+
living contract around the slot you are changing, and the merge upserts every
|
|
206
|
+
operation the document spells. So the collision on this axis needs no overlap at
|
|
207
|
+
all. Two features touching one service, editing DIFFERENT operations, destroy
|
|
208
|
+
each other — the second to archive quietly pushes its authoring-time copy of the
|
|
209
|
+
operation it never meant to touch back over whatever landed in between.
|
|
210
|
+
|
|
211
|
+
The same marker fixes it, as a vendor extension beside \`x-loam-remove\`:
|
|
212
|
+
|
|
213
|
+
\`\`\`yaml
|
|
214
|
+
paths:
|
|
215
|
+
/orders/{id}/cancel:
|
|
216
|
+
post:
|
|
217
|
+
operationId: cancelOrder
|
|
218
|
+
x-loam-based-on: 86feabe621ed887d
|
|
219
|
+
\`\`\`
|
|
220
|
+
|
|
221
|
+
\`loam rebase\` writes one on EVERY operation in the delta, pinned to the living
|
|
222
|
+
version. That is what lets the merge tell a QUOTE from an EDIT:
|
|
223
|
+
|
|
224
|
+
- pin equals the operation's own content → you quoted it → **the merge skips it
|
|
225
|
+
entirely** and the living contract keeps whatever it holds. Archive prints
|
|
226
|
+
\`· quotes …\` so the plan never silently writes less than your delta spells.
|
|
227
|
+
- pin equals the living operation → you edited it, nobody else did → merged.
|
|
228
|
+
- pin matches neither → you edited it AND somebody landed a change to it →
|
|
229
|
+
\`openapi.baseline-stale\`, refused.
|
|
230
|
+
|
|
231
|
+
Not covered, deliberately, and worth knowing: path-level keys (\`parameters\`,
|
|
232
|
+
\`servers\`) and \`components/schemas\` are still upserted wholesale, so a restated
|
|
233
|
+
schema can still revert another feature's change to it —
|
|
234
|
+
\`openapi.path-item-modified\` and \`openapi.component-modified\` are your only
|
|
235
|
+
warning there. Removal markers carry no pin either; \`openapi.remove-target-mismatch\`
|
|
236
|
+
is what guards those.
|
|
237
|
+
|
|
238
|
+
## Drawing a shared broker
|
|
239
|
+
|
|
240
|
+
A broker drawn as ONE element is the node every service in the fleet points at,
|
|
241
|
+
and any view over it is a star nobody can read. Nothing in loam catches this —
|
|
242
|
+
loam parses the model and renders no view, so the map can be perfectly valid and
|
|
243
|
+
completely illegible. Two habits keep it readable, and both are cheap only
|
|
244
|
+
before the fleet is drawn:
|
|
245
|
+
|
|
246
|
+
**Model the topic, not the broker.** Nest a topic per channel inside the broker's
|
|
247
|
+
element and point the edges at it — \`paymentService -> kafka.paymentEvents\`, not
|
|
248
|
+
\`paymentService -> kafka\`. One hub of degree sixty becomes twelve of degree
|
|
249
|
+
five, and it is the truer model besides: what a producer and a consumer share is
|
|
250
|
+
the topic, never the broker. Every check joins the same way, because an edge into
|
|
251
|
+
a nested element resolves to the element that owns it. Declare the kind once and
|
|
252
|
+
tag it there —
|
|
253
|
+
|
|
254
|
+
specification {
|
|
255
|
+
element topic {
|
|
256
|
+
#external
|
|
257
|
+
style { shape queue }
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
— because **LikeC4 does not inherit tags**: a topic nested inside an \`#external\`
|
|
262
|
+
broker is not external itself, and \`loam validate --all\` will ask for a
|
|
263
|
+
\`services/payment.events/\` nobody owes (\`landscape.service-undocumented\`).
|
|
264
|
+
|
|
265
|
+
**Scope the views.** \`exclude element.tag = #external\` keeps the fleet overview to
|
|
266
|
+
the calls between our own services; a second view over the broker draws the event
|
|
267
|
+
spine on its own. Views are LikeC4's and loam computes none, so this costs loam
|
|
268
|
+
nothing and is worth doing anyway — an \`include *\` over the whole fleet map is
|
|
269
|
+
also the one view whose computation takes minutes rather than milliseconds. The
|
|
270
|
+
scaffolded landscape applies the same pruning to ubiquitous infrastructure with
|
|
271
|
+
\`#platform\`: the fleet view excludes it, and the platform view's
|
|
272
|
+
\`include * -> element.tag = #platform\` (the relationship form — the obvious
|
|
273
|
+
spelling draws boxes with no edges) keeps "who depends on it" answerable.
|
|
274
|
+
|
|
275
|
+
## A message produced outside the fleet
|
|
276
|
+
|
|
277
|
+
A platform service that is not ours — a config service, an auth broker —
|
|
278
|
+
produces messages our services consume, and no \`services/<id>/\` will ever hold
|
|
279
|
+
its contract. Declare the production on the landscape, where the element
|
|
280
|
+
already says \`#external\`:
|
|
281
|
+
|
|
282
|
+
externalConfig = softwareSystem 'external-config' {
|
|
283
|
+
#external
|
|
284
|
+
}
|
|
285
|
+
externalConfig -> kafka.configTopic 'publishes config refreshes' {
|
|
286
|
+
metadata { publishes 'config.ConfigRefreshed' }
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
\`spine.message-unproduced\` then stops firing — the message HAS a producer,
|
|
290
|
+
outside the fleet — and the contract question moves to the one file that can
|
|
291
|
+
answer it: the consuming service's own asyncapi.yaml. While that file declares
|
|
292
|
+
no shape for the message, \`spine.message-external\` (warn) says so; once the
|
|
293
|
+
consumer declares the payload, the axis is quiet. A truthful map and a green
|
|
294
|
+
build no longer exclude each other, which is the exact choice this used to force.
|
|
295
|
+
`,
|
|
296
|
+
};
|
|
297
|
+
//# sourceMappingURL=spine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spine.js","sourceRoot":"","sources":["../../../../../src/core/agent/workflows/reference/spine.ts"],"names":[],"mappings":"AAqBA,MAAM,CAAC,MAAM,UAAU,GAAmB;IACxC,IAAI,EAAE,YAAY;IAClB,WAAW,EAAE,mFAAmF;IAChG,0EAA0E;IAC1E,4EAA4E;IAC5E,yEAAyE;IACzE,YAAY,EAAE,EAAE;IAChB,OAAO,EACL,qNAAqN;IACvN,UAAU,EAAE,8BAA8B;IAC1C,YAAY,EAAE,EAAE;IAChB,KAAK,EAAE;QACL,cAAc;QACd,wCAAwC;QACxC,yBAAyB;QACzB,sCAAsC;KACvC;IACD,IAAI,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsRP;CACA,CAAC","sourcesContent":["/**\n * The `loam-spine` reference page: how the artifacts JOIN.\n *\n * Operation and message ids, coverage and permission references, the\n * capability tree and its `Realizes:` join, the requirement baseline pins\n * (`Based-On:` / `x-loam-based-on`), how to draw a shared broker, and how to\n * declare a message produced outside the fleet.\n *\n * This is the grammar a reader consults while writing ONE document, which is\n * why it is a printed page rather than a section of AGENTS.md: it is consulted\n * at a moment you can name, and paying for it on every invocation of every\n * session bought nothing. `## Which element IS which service` deliberately\n * stayed behind in ../../agents-md/spine.ts — an agent cannot read the\n * landscape at all without it, so it is orientation rather than reference.\n *\n * The text is the AGENTS.md section verbatim; nothing was rewritten to move\n * it, so a cross-reference from the remaining file still names the same\n * headings. See ./reference.ts for why these four pages exist.\n */\nimport type { CommandContent } from \"../../contract.js\";\n\nexport const LOAM_SPINE: CommandContent = {\n name: \"loam-spine\",\n description: \"Reference: the ID spine — every join between the artifacts, and the baseline pins\",\n // A reference page takes no arguments: it is one document, printed whole.\n // The empty hint is what `loam instructions` prints beside the name, and an\n // invented one (\"<none>\") would read as an argument somebody could pass.\n argumentHint: \"\",\n purpose:\n \"The exact joins the artifacts are held together by — operationId, message name, `Covers:`, `Requires:`, `Capability:`/`Realizes:` — and the `Based-On:` pins that stop two features in flight reverting each other.\",\n invocation: \"loam instructions loam-spine\",\n placeholders: [],\n spine: [\n \"The ID spine\",\n \"The requirement baseline — `Based-On:`\",\n \"Drawing a shared broker\",\n \"A message produced outside the fleet\",\n ],\n body: `The ID spine: how the artifacts join, and what pins a change to the version it was written against.\n\n## The ID spine\n\nThe spine is a family of exact joins, not one magic id: feature tag; service\nbinding; synchronous operation; asynchronous message; architecture coverage;\nauthorization permission. Three artifacts describe the same synchronous call in\nthree languages. They are joined by the **operationId**, and that join is what\n\\`loam validate\\` checks:\n\n- architecture — a C4 edge names the operation it calls:\n \\`checkoutWeb -> paymentService 'Calls authorizePayment' { metadata { op 'authorizePayment' } }\\`\n- behaviour — a requirement declares the operations it governs:\n \\`Operations: authorizePayment\\`\n- contract — the provider's \\`openapi.yaml\\` defines it:\n \\`operationId: authorizePayment\\`\n\nSpell it identically in all three places. A mismatch is an error, not a warning:\nan edge calling an operation nobody defines is a broken contract between services.\n\nFeatures are joined the same way: a delta's new elements and edges carry the\nfeature id as a LikeC4 tag (\\`#FEAT-101\\`), and that tag is exactly what\n\\`loam archive\\` folds into the living landscape. Untagged elements in a delta are\ncontext for the diagram, not changes.\n\nThe other joins use the same discipline:\n\n- \\`metadata { publishes|consumes 'message' }\\` on an edge ↔\n \\`Publishes:\\`/\\`Consumes:\\` in a requirement ↔ the AsyncAPI 3 message name;\n- \\`Covers:\\` in an architecture requirement ↔ a C4 element/edge or\n \\`alert:<id>\\` / \\`sli:<id>\\` from health.yaml;\n- \\`Requires: <subject>/<permission>\\` in either requirement file ↔ the fleet\n declaration in \\`architecture/permissions.yaml\\`;\n- \\`Capability:\\` in either requirement file ↔ a declared id in\n \\`architecture/capabilities.yaml\\`. A LIST, comma-separated — the relation is\n many-to-many in both directions.\n\nThe permissions file is opt-in: a fleet with no \\`Requires:\\` line owes none.\nOnce a requirement names a permission, an undeclared pair is\n\\`permissions.unknown\\` (error). An unreadable vocabulary is\n\\`permissions.invalid\\`; a declaration nothing requires is\n\\`permissions.unenforced\\` (warn). The subject is what the permission is checked\nON (user, profile, service), not necessarily the caller. \\`owned_by\\` and\n\\`enforced_by\\` are explanatory and are not resolved against services; this is\na checked document join, not proof that the identity provider implements it.\n\nThe capability axis opts in the OTHER way: the FILES are the opt-in, not the\nline. A fleet with neither \\`architecture/capabilities.yaml\\` nor a\n\\`capabilities/\\` directory gets no capability findings at all, however many\n\\`Capability:\\` lines its requirements already carry.\n\nA capability may be declared on either side, and the vocabulary is the UNION.\nA name alone is a line in \\`architecture/capabilities.yaml\\` —\n\\`capabilities: {<id>: {description, owner}}\\`, nested ids such as\n\\`payments/refunds\\` kept as one flat key. A name with prose behind it gets a\ndocument instead: \\`capabilities/<id>/spec.md\\`, the AUTHORED business tree,\none directory per capability with nesting spelled by the tree\n(\\`capabilities/payments/refunds/spec.md\\`). The directory is the list — there\nis no manifest — and its existence is what opts the fleet in, so \\`loam init\\`\ndoes not scaffold it. A directory holding neither the document nor a\ncapability beneath it is \\`capability.doc-missing\\` (warn).\n\nThe document carries narrative and then \\`## Requirements\\`, in the same\ngrammar every spec.md uses, with two rules of its own. Every requirement needs\na \\`Requirement-ID:\\` — these documents outlive the services that realize them,\nso identity is the line and not the heading; without one it is\n\\`capability.requirement-unidentified\\` (error). And a capability requirement\nmust be observable OUTSIDE the fleet: \\`Operations:\\`, \\`Covers:\\`,\n\\`Publishes:\\` and \\`Consumes:\\` all resolve against one service's own contract,\nso carrying any of them is \\`capability.requirement-service-scoped\\` (error) —\nwrite it in that service's spec.md instead. Naming no service is an authoring\nrule that PR review holds, not one loam checks: matching service names in prose\nwould be a heuristic, and loam does not guess. The axis's own two joins are\nrefused here as well (\\`capability.requirement-inert-join\\`, error): they point\nINTO the tree, so nothing reads them written inside it.\n\nA FEATURE changes a capability document with a delta, not by editing it:\n\\`features/<FEAT>/capabilities/<id>/spec.md\\`, the same\n\\`## ADDED|MODIFIED|REMOVED Requirements\\` grammar the service deltas use, the\nsame nesting (\\`features/<FEAT>/capabilities/payments/refunds/\\`), the same\n\\`Based-On:\\` pins, and the same \\`delta.*\\` refusals with the capability id as\nthe subject. \\`scaffold.placeholder\\` reads it too, so an unedited template\nheading refuses the archive here as it does in a spec delta, and\n\\`capability.doc-missing\\` (warn) names a directory in it that holds no\n\\`spec.md\\` — an empty one is not a delta, so the merge would carry nothing.\n\\`loam archive\\` merges it into the living document in the same\ntransaction as the service merge, creating \\`capabilities/<id>/spec.md\\` — and\nso the whole tree — when this is the first feature to mention it; the heading\nis the id and the narrative above \\`## Requirements\\` is yours to write, because\nloam merges requirements and never prose. The document's three rules above are\ngraded on the delta BEFORE the merge, so a capability requirement filed at the\nwrong altitude is refused while you still hold it. Editing the living document\ndirectly is still legal; the delta is what makes two features touching one\npromise collide loudly instead of silently. \\`loam new <FEAT> --capability <id>\\`\nscaffolds that delta, and \\`loam show <FEAT>\\` and \\`loam delta <FEAT>\\` list the\npromises a feature changes — a capability delta names no service, so without\nthose two a business-only feature reads as a feature carrying nothing.\n\nA service requirement says which promise it serves with \\`Realizes:\\`, entries\nspelled \\`<capability-id>#<Requirement-ID>\\`. This is the join the tree exists\nfor, and it is NOT the same claim as \\`Capability:\\` beside it — that names a\ntheme, this names one promise, and a requirement commonly carries both. The\ncapability half of an entry is what makes the target addressable, since a\n\\`Requirement-ID\\` is unique only inside its own document; the separator is the\nLAST \\`#\\`, because the requirement half's grammar excludes one and the\ncapability half's does not. Written by whoever implements the requirement,\nnever by the analyst — a business document must not be edited every time the\nfleet rearranges which service carries which part.\n\nBoth directions are graded. An entry naming no capability requirement is\n\\`capability.realizes-unknown\\` (error; in a feature delta it gates\n\\`loam archive\\`, \\`--approve\\`-overridable), and its message says which of the\nfive failures happened — malformed entry, undeclared capability, declared but\nundocumented, document with no requirements, or an id that document does not\ndeclare. A capability requirement nothing keeps is\n\\`capability.requirement-unrealized\\` (warn, one per requirement): it never\ngates, because writing the business document ahead of the fleet is the intended\nuse. \\`loam list capabilities --json\\` carries the whole join — each capability's\nrequirements, and what realizes each one.\n\n\\`Realizes:\\` is not the only way to keep a promise, and for a criterion that\nCROSSES services it is not enough: \"I enter a login and a password and I am in\"\nbelongs to no single service's spec, because each promises only its own part. A\nUSE CASE can carry it, because it IS the hop sequence. So a \\`dynamic view\\`\nalready tagged \\`#cap-<slug>\\` may carry \\`#req-<slug>\\` as well, naming one of\nthat capability's requirements — the architect's answer to the analyst's\npromise, graded by the same four \\`usecase.*\\` codes down to the operation. The\nsecond tag is SCOPED by the first: a \\`Requirement-ID\\` is unique only inside its\nown document, so a \\`#req-\\` tag on a view that resolves no capability, or two, is\n\\`usecase.requirement-unresolved\\` (error) rather than a guess. Several \\`#req-\\`\ntags on one view are legal — a flow commonly keeps two promises. Both tags spell\ntheir id with every character outside \\`[A-Za-z0-9_-]\\` flattened to \\`-\\`,\nbecause that is all a LikeC4 tag name accepts.\n\nAgainst that vocabulary an undeclared name is \\`capability.unknown\\` (error; in\na feature delta it gates \\`loam archive\\`, \\`--approve\\`-overridable), an\nunreadable \\`capabilities.yaml\\` is \\`capability.invalid\\` exactly once per run\nwith every grade that resolves against the vocabulary suspended behind it, and\na declared capability no living requirement realizes is\n\\`capability.unrealized\\` (warn, one per capability). The fleet total is\nreadable: \\`loam list capabilities\\` reports each capability's realizing\nrequirements, their services and the draft/verified split, and\n\\`loam explore --capability <id>\\` seeds an exploration from the realizing\nservices.\n\n## The requirement baseline — \\`Based-On:\\`\n\nA MODIFIED requirement carries its FULL new text, not a diff, so archive does not\nmerge your wording into the living one — it REPLACES it. When two features rewrite\nthe same requirement, the loser loses everything they wrote, scenarios and all.\n\n\\`loam validate\\` names the other feature while both are in flight\n(\\`delta.modified-conflict\\`), but that warning cannot see the case that actually\nhappens: the first feature archives, stops being an active feature, the second\nrevalidates GREEN, and its archive lands text written against a document that no\nlonger exists — \\`+0 ~1 -0\\`, exit 0, nobody told.\n\nSo a MODIFIED or REMOVED requirement pins the living version it was written\nagainst, directly under its identity:\n\n\\`\\`\\`markdown\n### Requirement: Cancel an order\nRequirement-ID: REQ-042\nBased-On: 3bcc7a40f2b23ff3\n\\`\\`\\`\n\nYou do not compute that value — \\`loam rebase FEAT-101\\` writes it, for every\nMODIFIED/REMOVED requirement in the feature, from the living text as it stands\nright now. Run it when you finish authoring a delta, and again after you resolve\na collision. Archive refuses on a stale pin (\\`delta.baseline-stale\\`) and says\nboth digests.\n\n**Restamping is not resolving.** A pin claims \"I read this version\". If\n\\`loam rebase\\` reports a requirement as moved, re-read the living requirement and\nfold in what you still mean BEFORE you ship — otherwise you have re-pinned to a\ndocument you never read, and archive will merge your text over theirs with\nloam's blessing. There is no automatic three-way merge and there will not be one:\nrequirement prose does not merge, people do.\n\nA delta adopted from OpenSpec has no such line and gets a warning\n(\\`delta.baseline-missing\\`) — one that GATES the archive: unpinned is exactly\nthe shape that reverts someone else's landed change at exit 0. A migrated\ncorpus is not stranded by this: \\`loam rebase <FEAT>\\` pins every delta in one\ncommand, and a human's \\`--approve\\` archives unpinned when the loss is meant.\n\n### The same pin on the contract axis — and why it matters more there\n\nA feature's \\`openapi.yaml\\` is a COMPLETE document, not a patch: you restate the\nliving contract around the slot you are changing, and the merge upserts every\noperation the document spells. So the collision on this axis needs no overlap at\nall. Two features touching one service, editing DIFFERENT operations, destroy\neach other — the second to archive quietly pushes its authoring-time copy of the\noperation it never meant to touch back over whatever landed in between.\n\nThe same marker fixes it, as a vendor extension beside \\`x-loam-remove\\`:\n\n\\`\\`\\`yaml\npaths:\n /orders/{id}/cancel:\n post:\n operationId: cancelOrder\n x-loam-based-on: 86feabe621ed887d\n\\`\\`\\`\n\n\\`loam rebase\\` writes one on EVERY operation in the delta, pinned to the living\nversion. That is what lets the merge tell a QUOTE from an EDIT:\n\n- pin equals the operation's own content → you quoted it → **the merge skips it\n entirely** and the living contract keeps whatever it holds. Archive prints\n \\`· quotes …\\` so the plan never silently writes less than your delta spells.\n- pin equals the living operation → you edited it, nobody else did → merged.\n- pin matches neither → you edited it AND somebody landed a change to it →\n \\`openapi.baseline-stale\\`, refused.\n\nNot covered, deliberately, and worth knowing: path-level keys (\\`parameters\\`,\n\\`servers\\`) and \\`components/schemas\\` are still upserted wholesale, so a restated\nschema can still revert another feature's change to it —\n\\`openapi.path-item-modified\\` and \\`openapi.component-modified\\` are your only\nwarning there. Removal markers carry no pin either; \\`openapi.remove-target-mismatch\\`\nis what guards those.\n\n## Drawing a shared broker\n\nA broker drawn as ONE element is the node every service in the fleet points at,\nand any view over it is a star nobody can read. Nothing in loam catches this —\nloam parses the model and renders no view, so the map can be perfectly valid and\ncompletely illegible. Two habits keep it readable, and both are cheap only\nbefore the fleet is drawn:\n\n**Model the topic, not the broker.** Nest a topic per channel inside the broker's\nelement and point the edges at it — \\`paymentService -> kafka.paymentEvents\\`, not\n\\`paymentService -> kafka\\`. One hub of degree sixty becomes twelve of degree\nfive, and it is the truer model besides: what a producer and a consumer share is\nthe topic, never the broker. Every check joins the same way, because an edge into\na nested element resolves to the element that owns it. Declare the kind once and\ntag it there —\n\n specification {\n element topic {\n #external\n style { shape queue }\n }\n }\n\n— because **LikeC4 does not inherit tags**: a topic nested inside an \\`#external\\`\nbroker is not external itself, and \\`loam validate --all\\` will ask for a\n\\`services/payment.events/\\` nobody owes (\\`landscape.service-undocumented\\`).\n\n**Scope the views.** \\`exclude element.tag = #external\\` keeps the fleet overview to\nthe calls between our own services; a second view over the broker draws the event\nspine on its own. Views are LikeC4's and loam computes none, so this costs loam\nnothing and is worth doing anyway — an \\`include *\\` over the whole fleet map is\nalso the one view whose computation takes minutes rather than milliseconds. The\nscaffolded landscape applies the same pruning to ubiquitous infrastructure with\n\\`#platform\\`: the fleet view excludes it, and the platform view's\n\\`include * -> element.tag = #platform\\` (the relationship form — the obvious\nspelling draws boxes with no edges) keeps \"who depends on it\" answerable.\n\n## A message produced outside the fleet\n\nA platform service that is not ours — a config service, an auth broker —\nproduces messages our services consume, and no \\`services/<id>/\\` will ever hold\nits contract. Declare the production on the landscape, where the element\nalready says \\`#external\\`:\n\n externalConfig = softwareSystem 'external-config' {\n #external\n }\n externalConfig -> kafka.configTopic 'publishes config refreshes' {\n metadata { publishes 'config.ConfigRefreshed' }\n }\n\n\\`spine.message-unproduced\\` then stops firing — the message HAS a producer,\noutside the fleet — and the contract question moves to the one file that can\nanswer it: the consuming service's own asyncapi.yaml. While that file declares\nno shape for the message, \\`spine.message-external\\` (warn) says so; once the\nconsumer declares the payload, the axis is quiet. A truthful map and a green\nbuild no longer exclude each other, which is the exact choice this used to force.\n`,\n};\n"]}
|