@ybotok/loam 0.1.0-beta.2 → 0.2.0-alpha.1
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 +2570 -61
- 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 +494 -0
- package/SCHEMA.md +2362 -245
- 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 +317 -0
- package/dist/commands/archive/plan/gate.js.map +1 -0
- package/dist/commands/archive/plan/landscape.js +161 -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 +251 -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 +82 -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 +194 -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 +236 -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 +250 -0
- package/dist/commands/validate/service/service.js.map +1 -0
- package/dist/commands/validate/service/specs.js +219 -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/agent/agents-stamp.js +155 -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 +334 -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/read.js +239 -0
- 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 +47 -0
- package/dist/core/brief/unchecked.js.map +1 -0
- package/dist/core/c4/arch.js +282 -0
- package/dist/core/c4/arch.js.map +1 -0
- package/dist/core/c4/likec4.js +88 -0
- package/dist/core/c4/likec4.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 +183 -0
- package/dist/core/c4/project/load.js.map +1 -0
- package/dist/core/c4/resolve/service.js +113 -0
- package/dist/core/c4/resolve/service.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 +126 -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 +172 -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 +251 -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/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 +59 -55
- 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 +132 -77
- 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 +118 -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 +192 -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 +236 -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 +171 -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 +187 -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 +235 -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 +55 -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 -291
- 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 -254
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/unarchive.js.map +0 -1
- package/dist/commands/validate.js +0 -1591
- 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 -343
- package/dist/commands/vouch.js.map +0 -1
- package/dist/core/agent.js +0 -2065
- package/dist/core/agent.js.map +0 -1
- package/dist/core/agents-stamp.js +0 -68
- 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/brief.js +0 -733
- 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 -559
- 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 -479
- 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 -720
- package/dist/core/provenance.js.map +0 -1
- package/dist/core/records.js.map +0 -1
- package/dist/core/repo.js +0 -436
- package/dist/core/repo.js.map +0 -1
- package/dist/core/report.js.map +0 -1
- package/dist/core/spec.js +0 -481
- 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 -1104
- 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,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does this spec still describe the code it points at?
|
|
3
|
+
*
|
|
4
|
+
* One spec, read against the sources it declares. It answers only about what it
|
|
5
|
+
* could check, and says which sources it skipped — a vouch over a spec whose
|
|
6
|
+
* sources could not be read would be the claim this command exists to prevent.
|
|
7
|
+
*/
|
|
8
|
+
import { listField, parseFrontmatter } from "../../../core/document/frontmatter.js";
|
|
9
|
+
import { missingSources, patternSources, unsafeSources } from "../../../core/provenance/sources.js";
|
|
10
|
+
import { emptySourcesMessage, sourcesDigest } from "../../../core/provenance/stamp.js";
|
|
11
|
+
import { NotUtf8Error, readUtf8 } from "../../../core/staging/writes.js";
|
|
12
|
+
/**
|
|
13
|
+
* "There is no living spec here", in one sentence.
|
|
14
|
+
*
|
|
15
|
+
* Three callers reach this state independently — the stamp (`../run.ts`), the
|
|
16
|
+
* reading list (`../sample/plan.ts`) and the reading pack (`../pack/pack.ts`)
|
|
17
|
+
* — and two of them carried a comment promising to keep the wording identical
|
|
18
|
+
* to the third, which is a promise nothing could enforce. It lives in the leaf
|
|
19
|
+
* all three already import, because the failure this repo has actually shipped
|
|
20
|
+
* is four copies of an enumeration `catch` that looked identical for years
|
|
21
|
+
* while a fix landed in one of them: a `--sample` run refusing a missing spec
|
|
22
|
+
* with older advice than a bare run gives is the same defect, one flag wide.
|
|
23
|
+
*/
|
|
24
|
+
export function noLivingSpecMessage(specPath, service) {
|
|
25
|
+
return `No living spec at ${specPath}. Run \`loam adopt\` for '${service}' first.`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The per-file half of the refusal discipline: everything vouch cannot verify
|
|
29
|
+
* about ONE spec file, checked the same way for spec.md and arch.spec.md.
|
|
30
|
+
*/
|
|
31
|
+
export async function verifySpec(req, path, file) {
|
|
32
|
+
// Through readUtf8, so the round trip below is exact: the stamp is computed
|
|
33
|
+
// from `raw` and the race check compares `Buffer.from(raw)` against the bytes
|
|
34
|
+
// on disk, which only agree if the file decoded without substitutions. A
|
|
35
|
+
// non-UTF-8 spec is refused by name rather than stamped as verified with
|
|
36
|
+
// every undecodable byte replaced. Refused HERE rather than left to the CLI's
|
|
37
|
+
// top-level handler, which would report a diagnosable state as `internal`.
|
|
38
|
+
// What it replaces is a diagnosis that was worse than none: a UTF-16 spec
|
|
39
|
+
// decoded to a document with no frontmatter loam could find, so vouch refused
|
|
40
|
+
// it as `sources-absent` — "names no sources" — over a file whose `sources:`
|
|
41
|
+
// line is right there.
|
|
42
|
+
let raw;
|
|
43
|
+
try {
|
|
44
|
+
raw = await readUtf8(path);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
if (!(err instanceof NotUtf8Error))
|
|
48
|
+
throw err;
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
code: "repository-unavailable",
|
|
52
|
+
message: `${req.service}: ${file} cannot be vouched for — ${err.message}`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const fm = parseFrontmatter(raw);
|
|
56
|
+
// Before any sources reasoning: a header that does not parse hides whatever
|
|
57
|
+
// fields the author wrote, so "names no sources" would be a false diagnosis —
|
|
58
|
+
// and if the file ever reached the stamp, withFrontmatterFields' rule for
|
|
59
|
+
// unreadable headers is replace-don't-merge, which would silently discard the
|
|
60
|
+
// author's owner/service/sources lines. Refuse before the write path can see
|
|
61
|
+
// the file. The code stays `sources-absent` — for vouch's purposes the
|
|
62
|
+
// sources ARE unreadable-hence-absent, and the message carries the real
|
|
63
|
+
// diagnosis — rather than minting a new ErrorCode for one refusal.
|
|
64
|
+
if (fm.malformed) {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
code: "sources-absent",
|
|
68
|
+
message: `${req.service}: ${file} has a frontmatter block that cannot be read as YAML — ` +
|
|
69
|
+
`its fields (\`sources\` included) are unreadable, and stamping would rewrite the header wholesale, ` +
|
|
70
|
+
`losing what the author wrote. Fix the YAML between the \`---\` fences ` +
|
|
71
|
+
`(\`loam validate\` reports it as \`frontmatter.malformed\`), then re-run.`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const sources = listField(fm, "sources");
|
|
75
|
+
if (sources.length === 0) {
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
code: "sources-absent",
|
|
79
|
+
message: `${req.service}: ${file} names no sources — there is nothing to vouch it against. Add \`sources:\` naming the code it was written from.`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
// spec.md's refusals predate the second axis and keep their exact wording;
|
|
83
|
+
// where they did not name the file, only the newer axis adds it.
|
|
84
|
+
const label = file === "spec.md" ? req.service : `${req.service}: ${file}`;
|
|
85
|
+
// Before the existence check: a pattern is not a path that "does not exist",
|
|
86
|
+
// it is an entry loam no longer reads at all, and the refusal has to say so.
|
|
87
|
+
const patterns = patternSources(sources);
|
|
88
|
+
if (patterns.length > 0) {
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
code: "sources-path-missing",
|
|
92
|
+
message: `${label}: ${patterns.length} source(s) are glob patterns — ${patterns.join(", ")}. Patterns are no longer supported: name files or directories (a directory already covers everything beneath it). A stamp over a pattern would vouch for a file set nobody can be sure of.`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const unsafe = unsafeSources(req.repoDir, sources);
|
|
96
|
+
if (unsafe.length > 0) {
|
|
97
|
+
return {
|
|
98
|
+
ok: false,
|
|
99
|
+
code: "sources-path-missing",
|
|
100
|
+
message: `${label}: ${unsafe.length} source(s) escape the service repo — ${unsafe.join(", ")}. Sources must be relative paths contained by this repository, including through symlinks; vouch will not hash files outside it.`,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
const missing = missingSources(req.repoDir, sources);
|
|
104
|
+
if (missing.length > 0) {
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
code: "sources-path-missing",
|
|
108
|
+
message: `${label}: ${missing.length} source(s) do not exist — ${missing.join(", ")}. Vouching now would stamp a claim about code that is not there.`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const { digest, index, skipped } = await sourcesDigest(req.repoDir, sources);
|
|
112
|
+
if (index.length === 0) {
|
|
113
|
+
// A directory that exists but holds no files — or only ones the walk leaves
|
|
114
|
+
// out: dot-entries, `node_modules`, anything the repository itself ignores.
|
|
115
|
+
// The paths "resolve", but a digest over nothing never changes, so the stamp
|
|
116
|
+
// would read as current forever. The sentence comes from provenance/stamp.ts so
|
|
117
|
+
// that `loam validate` can grade the same state in the same words instead of
|
|
118
|
+
// going green on a document this command refuses.
|
|
119
|
+
return { ok: false, code: "sources-absent", message: emptySourcesMessage(label, sources) };
|
|
120
|
+
}
|
|
121
|
+
return { ok: true, path, file, raw, sources, digest, index, skipped };
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../../src/commands/vouch/vet/verify.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Does this spec still describe the code it points at?\n *\n * One spec, read against the sources it declares. It answers only about what it\n * could check, and says which sources it skipped — a vouch over a spec whose\n * sources could not be read would be the claim this command exists to prevent.\n */\nimport { listField, parseFrontmatter } from \"../../../core/document/frontmatter.js\";\nimport type { PathableService } from \"../../../core/kernel/ids/service.js\";\nimport { missingSources, patternSources, unsafeSources } from \"../../../core/provenance/sources.js\";\nimport { emptySourcesMessage, sourcesDigest, type SourceIndexEntry } from \"../../../core/provenance/stamp.js\";\nimport type { SkippedSource } from \"../../../core/provenance/walk.js\";\nimport { NotUtf8Error, readUtf8 } from \"../../../core/staging/writes.js\";\n\nexport interface VerifiedSpec {\n ok: true;\n path: string;\n file: string;\n /** The file exactly as read — what the stamp is applied to. */\n raw: string;\n sources: string[];\n digest: string;\n /** Per-file shas, in digest order — what `sources_files` is stamped from. */\n index: SourceIndexEntry[];\n skipped: SkippedSource[];\n}\n\n/**\n * The two facts the vetting reads — not the whole VouchRequest, because the\n * reading pack (`../pack/pack.ts`) asks the same questions with no date or\n * identity to offer: a pack stamps nothing.\n */\nexport interface SpecVetting {\n service: PathableService;\n repoDir: string;\n}\n\n/**\n * The refusal half, spelled here rather than imported from `../contract.ts`:\n * this module is a LEAF two sibling consumers share — `run.ts` (the stamp) and\n * `pack/pack.ts` (the worklist) — which is why it lives in its own package,\n * and an import from the vouch package above it would close the exact\n * package-graph cycle the move exists to avoid. The codes are a subset of\n * VouchOutcome's refusal codes, so run.ts returns one of these unchanged.\n */\nexport interface SpecRefusal {\n ok: false;\n code: \"sources-absent\" | \"sources-path-missing\" | \"repository-unavailable\";\n message: string;\n}\n\n/**\n * \"There is no living spec here\", in one sentence.\n *\n * Three callers reach this state independently — the stamp (`../run.ts`), the\n * reading list (`../sample/plan.ts`) and the reading pack (`../pack/pack.ts`)\n * — and two of them carried a comment promising to keep the wording identical\n * to the third, which is a promise nothing could enforce. It lives in the leaf\n * all three already import, because the failure this repo has actually shipped\n * is four copies of an enumeration `catch` that looked identical for years\n * while a fix landed in one of them: a `--sample` run refusing a missing spec\n * with older advice than a bare run gives is the same defect, one flag wide.\n */\nexport function noLivingSpecMessage(specPath: string, service: string): string {\n return `No living spec at ${specPath}. Run \\`loam adopt\\` for '${service}' first.`;\n}\n\n/**\n * The per-file half of the refusal discipline: everything vouch cannot verify\n * about ONE spec file, checked the same way for spec.md and arch.spec.md.\n */\nexport async function verifySpec(\n req: SpecVetting,\n path: string,\n file: string,\n): Promise<VerifiedSpec | SpecRefusal> {\n // Through readUtf8, so the round trip below is exact: the stamp is computed\n // from `raw` and the race check compares `Buffer.from(raw)` against the bytes\n // on disk, which only agree if the file decoded without substitutions. A\n // non-UTF-8 spec is refused by name rather than stamped as verified with\n // every undecodable byte replaced. Refused HERE rather than left to the CLI's\n // top-level handler, which would report a diagnosable state as `internal`.\n // What it replaces is a diagnosis that was worse than none: a UTF-16 spec\n // decoded to a document with no frontmatter loam could find, so vouch refused\n // it as `sources-absent` — \"names no sources\" — over a file whose `sources:`\n // line is right there.\n let raw: string;\n try {\n raw = await readUtf8(path);\n } catch (err) {\n if (!(err instanceof NotUtf8Error)) throw err;\n return {\n ok: false,\n code: \"repository-unavailable\",\n message: `${req.service}: ${file} cannot be vouched for — ${err.message}`,\n };\n }\n const fm = parseFrontmatter(raw);\n // Before any sources reasoning: a header that does not parse hides whatever\n // fields the author wrote, so \"names no sources\" would be a false diagnosis —\n // and if the file ever reached the stamp, withFrontmatterFields' rule for\n // unreadable headers is replace-don't-merge, which would silently discard the\n // author's owner/service/sources lines. Refuse before the write path can see\n // the file. The code stays `sources-absent` — for vouch's purposes the\n // sources ARE unreadable-hence-absent, and the message carries the real\n // diagnosis — rather than minting a new ErrorCode for one refusal.\n if (fm.malformed) {\n return {\n ok: false,\n code: \"sources-absent\",\n message:\n `${req.service}: ${file} has a frontmatter block that cannot be read as YAML — ` +\n `its fields (\\`sources\\` included) are unreadable, and stamping would rewrite the header wholesale, ` +\n `losing what the author wrote. Fix the YAML between the \\`---\\` fences ` +\n `(\\`loam validate\\` reports it as \\`frontmatter.malformed\\`), then re-run.`,\n };\n }\n const sources = listField(fm, \"sources\");\n if (sources.length === 0) {\n return {\n ok: false,\n code: \"sources-absent\",\n message: `${req.service}: ${file} names no sources — there is nothing to vouch it against. Add \\`sources:\\` naming the code it was written from.`,\n };\n }\n\n // spec.md's refusals predate the second axis and keep their exact wording;\n // where they did not name the file, only the newer axis adds it.\n const label = file === \"spec.md\" ? req.service : `${req.service}: ${file}`;\n\n // Before the existence check: a pattern is not a path that \"does not exist\",\n // it is an entry loam no longer reads at all, and the refusal has to say so.\n const patterns = patternSources(sources);\n if (patterns.length > 0) {\n return {\n ok: false,\n code: \"sources-path-missing\",\n message: `${label}: ${patterns.length} source(s) are glob patterns — ${patterns.join(\", \")}. Patterns are no longer supported: name files or directories (a directory already covers everything beneath it). A stamp over a pattern would vouch for a file set nobody can be sure of.`,\n };\n }\n\n const unsafe = unsafeSources(req.repoDir, sources);\n if (unsafe.length > 0) {\n return {\n ok: false,\n code: \"sources-path-missing\",\n message: `${label}: ${unsafe.length} source(s) escape the service repo — ${unsafe.join(\", \")}. Sources must be relative paths contained by this repository, including through symlinks; vouch will not hash files outside it.`,\n };\n }\n\n const missing = missingSources(req.repoDir, sources);\n if (missing.length > 0) {\n return {\n ok: false,\n code: \"sources-path-missing\",\n message: `${label}: ${missing.length} source(s) do not exist — ${missing.join(\", \")}. Vouching now would stamp a claim about code that is not there.`,\n };\n }\n\n const { digest, index, skipped } = await sourcesDigest(req.repoDir, sources);\n if (index.length === 0) {\n // A directory that exists but holds no files — or only ones the walk leaves\n // out: dot-entries, `node_modules`, anything the repository itself ignores.\n // The paths \"resolve\", but a digest over nothing never changes, so the stamp\n // would read as current forever. The sentence comes from provenance/stamp.ts so\n // that `loam validate` can grade the same state in the same words instead of\n // going green on a document this command refuses.\n return { ok: false, code: \"sources-absent\", message: emptySourcesMessage(label, sources) };\n }\n return { ok: true, path, file, raw, sources, digest, index, skipped };\n}\n"]}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { loadConfig } from "../../core/envelope/config.js";
|
|
2
|
+
import { fail, NO_SERVICE_MESSAGE, reportNoConfig } from "../../core/envelope/json.js";
|
|
3
|
+
import { gitIdentity } from "../../core/provenance/git.js";
|
|
4
|
+
import { today } from "../../core/provenance/stamp.js";
|
|
5
|
+
import { serviceTreePathOf } from "../../core/repo/service-target.js";
|
|
6
|
+
import { runPack } from "./pack/print.js";
|
|
7
|
+
import { confirmVouch } from "./prompt/confirm.js";
|
|
8
|
+
import { emitStampJson, printStamp } from "./report.js";
|
|
9
|
+
import { vouch } from "./run.js";
|
|
10
|
+
import { buildSamplePlan } from "./sample/plan.js";
|
|
11
|
+
import { printReadingList } from "./sample/print.js";
|
|
12
|
+
export function registerVouch(program) {
|
|
13
|
+
program
|
|
14
|
+
.command("vouch")
|
|
15
|
+
.description("Vouch for a service's living specs: stamp spec.md, and arch.spec.md when present, verified against the code they describe")
|
|
16
|
+
.option("--service <id>", "service to vouch for (defaults to the configured service)")
|
|
17
|
+
.option("--yes", "skip the confirmation — required when stdin is not a terminal, and still records `vouched_by`")
|
|
18
|
+
.option("--pack", "print the re-vouch reading pack — the doc-body diff since the last vouch, the source files that moved, and the sections already covered — then exit without vouching; read-only, nothing is stamped")
|
|
19
|
+
.option("--sample <n>", "vouch after reading a deterministic sample of <n> sections per spec file — the stamp records `vouch_scope: sampled`, `loam list` shows `vouched (sampled)`, and `loam validate` reports `sources.sampled-vouch` until a full vouch clears it")
|
|
20
|
+
.option("--json", "emit the machine contract instead of the human view")
|
|
21
|
+
.action(async (opts) => {
|
|
22
|
+
const json = opts.json === true;
|
|
23
|
+
const loaded = await loadConfig();
|
|
24
|
+
if (loaded.kind !== "loaded") {
|
|
25
|
+
reportNoConfig(json, loaded);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const config = loaded.config;
|
|
29
|
+
const service = opts.service ?? config.service;
|
|
30
|
+
if (service === undefined) {
|
|
31
|
+
return fail(json, "invalid-option", NO_SERVICE_MESSAGE);
|
|
32
|
+
}
|
|
33
|
+
// Vouching hashes the code the doc was written from, so it can only run
|
|
34
|
+
// where that code is. From anywhere else the paths are someone else's.
|
|
35
|
+
//
|
|
36
|
+
// TWO conditions, two codes, and the split is the whole point. Both used
|
|
37
|
+
// to answer `invalid-option`, whose `loam explain` row says the
|
|
38
|
+
// invocation itself is wrong — contradictory flags, a mistyped one, a
|
|
39
|
+
// value that cannot be right. Here the invocation is perfectly correct
|
|
40
|
+
// and the REPOSITORY is the wrong one, so that code sent a user whose
|
|
41
|
+
// flags were fine off to re-read their flags, and left CI and agents
|
|
42
|
+
// unable to tell "run this in the service repo" from "you typed the flag
|
|
43
|
+
// wrong". `loam verify --record --service` already draws exactly this
|
|
44
|
+
// line for exactly this reason (see commands/verify/verify.ts); vouch
|
|
45
|
+
// and gherkin now answer the same pair, so a caller branching on
|
|
46
|
+
// `error.code` gets one answer from all three. The messages are
|
|
47
|
+
// unchanged — only the code moved.
|
|
48
|
+
if (config.service === undefined) {
|
|
49
|
+
return fail(json, "repository-unavailable", `Cannot vouch for '${service}' from here: this is not a service repo. \`sources\` resolve against the service's own repo — run it there.`);
|
|
50
|
+
}
|
|
51
|
+
if (config.service !== service) {
|
|
52
|
+
return fail(json, "service-mismatch", `Cannot vouch for '${service}' from here: this repo is '${config.service}'. \`sources\` resolve against the service's own repo — run it there.`);
|
|
53
|
+
}
|
|
54
|
+
// The repo root, not the cwd — see the comment on `repoDir` below; the
|
|
55
|
+
// identity has to be asked of the same directory the sources resolve in,
|
|
56
|
+
// or a subdirectory with its own git config would answer for it.
|
|
57
|
+
const repoDir = config.root ?? process.cwd();
|
|
58
|
+
// The flag's own grammar, before anything is read or hashed: `--sample`
|
|
59
|
+
// decides what the run CLAIMS, so a value nobody can read must not reach
|
|
60
|
+
// a stamp. Whole numbers only, and at least one — `0` is a vouch that
|
|
61
|
+
// read nothing, and there is already a way to record that (do not
|
|
62
|
+
// vouch). Checked before `--pack` below so a typo is refused the same
|
|
63
|
+
// way in both modes.
|
|
64
|
+
let sampleSize;
|
|
65
|
+
if (opts.sample !== undefined) {
|
|
66
|
+
if (!/^\d+$/.test(opts.sample.trim()) || Number(opts.sample) < 1) {
|
|
67
|
+
return fail(json, "invalid-option", `--sample expects a positive whole number of sections; got '${opts.sample}'. ` +
|
|
68
|
+
"Nothing was read and nothing was stamped.");
|
|
69
|
+
}
|
|
70
|
+
sampleSize = Number(opts.sample);
|
|
71
|
+
}
|
|
72
|
+
// The reading pack: read-only, so it runs BEFORE — and instead of —
|
|
73
|
+
// every write-path gate below (identity, TTY, `--yes`, lock, journal),
|
|
74
|
+
// which all defend the stamp; pack/pack.ts's banner carries the full
|
|
75
|
+
// reasoning. It runs AFTER the wrong-repo gate above on purpose: the
|
|
76
|
+
// source delta resolves `sources` here. `--yes` does not compose — it
|
|
77
|
+
// is the unattended stamp, and a pack that stamped would defeat the
|
|
78
|
+
// read it just prescribed.
|
|
79
|
+
if (opts.pack === true) {
|
|
80
|
+
if (opts.yes === true) {
|
|
81
|
+
return fail(json, "invalid-option", "--pack is the reading list and --yes is the unattended stamp; a pack that immediately " +
|
|
82
|
+
`stamped would defeat the read it just prescribed. Run --pack, read it, then \`loam vouch --service ${service}\`.`);
|
|
83
|
+
}
|
|
84
|
+
// `--sample` composes with the pack instead of conflicting with it:
|
|
85
|
+
// the pack is the reading list, and a sampled vouch's reading list is
|
|
86
|
+
// the sample. Passing it here makes the pack print the sections the
|
|
87
|
+
// subsequent `loam vouch --sample <n>` will stamp for — the same
|
|
88
|
+
// derivation from the same seed, so the two cannot prescribe
|
|
89
|
+
// different documents.
|
|
90
|
+
return runPack({ docsDir: config.docsDir, service: config.service, repoDir, json, sample: sampleSize });
|
|
91
|
+
}
|
|
92
|
+
// Who. Before any reading, because a run that cannot name a person has
|
|
93
|
+
// nothing to offer at the end of it and should not spend a digest finding
|
|
94
|
+
// that out.
|
|
95
|
+
const vouchedBy = await gitIdentity(repoDir);
|
|
96
|
+
if (vouchedBy === null) {
|
|
97
|
+
return fail(json, "vouch-unattributable", `Cannot vouch for '${service}': git names no \`user.email\` in ${repoDir}, so the stamp would ` +
|
|
98
|
+
"record a claim with nobody behind it — which is the one thing `status: verified` must not mean. " +
|
|
99
|
+
"Set it (`git config user.email you@example.com`) and re-run.");
|
|
100
|
+
}
|
|
101
|
+
// Whether a person is actually here. `vouch` is the only command in loam
|
|
102
|
+
// whose output is a claim about a HUMAN act — every other check is
|
|
103
|
+
// internal consistency, which fluent prose satisfies — and it used to run
|
|
104
|
+
// unattended, unattributed, twice in a row, stamping `verified` both
|
|
105
|
+
// times. Meanwhile the generated skill files pre-approved `Bash(loam:*)`,
|
|
106
|
+
// so the same agent that wrote the draft was permitted to promote it.
|
|
107
|
+
// That inverts loam's own argument about test evidence: an agent must not
|
|
108
|
+
// be able to SAY a scenario is tested, and it must not be able to say a
|
|
109
|
+
// spec matches the code either. The allowlist no longer covers this
|
|
110
|
+
// command (core/agent/tools/dialects.ts), and nothing but a terminal or an explicit
|
|
111
|
+
// `--yes` gets past here.
|
|
112
|
+
const attended = opts.yes === true;
|
|
113
|
+
if (!attended) {
|
|
114
|
+
if (process.stdin.isTTY !== true) {
|
|
115
|
+
return fail(json, "vouch-unattended", `Cannot vouch for '${service}' with nothing on the other end of stdin: this command records that ` +
|
|
116
|
+
"a PERSON read the code and says the document matches it, and nobody was asked. Run it from a " +
|
|
117
|
+
"terminal, or pass --yes to state that you are standing behind it anyway — `vouched_by` records " +
|
|
118
|
+
`${vouchedBy} either way.`);
|
|
119
|
+
}
|
|
120
|
+
// `--json` and a prompt do not compose: the envelope is one JSON
|
|
121
|
+
// document on stdout, and a question printed beside it is not parseable
|
|
122
|
+
// by the consumer that asked for it. A JSON caller is a program, so it
|
|
123
|
+
// gets the same answer a pipe does — say so with --yes.
|
|
124
|
+
if (json) {
|
|
125
|
+
return fail(json, "vouch-unattended", `Cannot vouch for '${service}' in --json mode without --yes: the confirmation is a question for a ` +
|
|
126
|
+
"person, and it cannot be asked on a stream whose whole contract is one JSON document.");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// The reading list is built BEFORE the question and AFTER the attendance
|
|
130
|
+
// gates: a run that cannot ask anybody anything must not spend two
|
|
131
|
+
// source-tree digests finding that out, and a person must not be asked
|
|
132
|
+
// to confirm a sample nobody has shown them. Every refusal a vouch can
|
|
133
|
+
// raise about these documents fires in here, so it lands before the read
|
|
134
|
+
// rather than after it.
|
|
135
|
+
let sample;
|
|
136
|
+
if (sampleSize !== undefined) {
|
|
137
|
+
const built = await buildSamplePlan({ docsDir: config.docsDir, service: config.service, repoDir, n: sampleSize });
|
|
138
|
+
if (!built.ok)
|
|
139
|
+
return fail(json, built.code, built.message);
|
|
140
|
+
sample = built.plan;
|
|
141
|
+
if (!json)
|
|
142
|
+
printReadingList(sample, service);
|
|
143
|
+
}
|
|
144
|
+
const servicePath = await serviceTreePathOf(config.docsDir, service);
|
|
145
|
+
if (!attended && !(await confirmVouch({ service, vouchedBy, docsDir: config.docsDir, servicePath, sample }))) {
|
|
146
|
+
return fail(json, "vouch-declined", `Nothing was stamped for '${service}'.`);
|
|
147
|
+
}
|
|
148
|
+
const outcome = await vouch({
|
|
149
|
+
docsDir: config.docsDir,
|
|
150
|
+
// The guard above proved `service` IS `config.service`, and only the
|
|
151
|
+
// latter carries the parse (`loam.json`'s `service` field is validated
|
|
152
|
+
// at load). Same string, but only one spelling is pathable.
|
|
153
|
+
service: config.service,
|
|
154
|
+
vouchedBy,
|
|
155
|
+
// The repo root, not the cwd. `sources:` are spelled relative to the
|
|
156
|
+
// repository — that is what they mean in the frontmatter and what
|
|
157
|
+
// `loam validate` resolves them against — so vouching from a
|
|
158
|
+
// subdirectory used to report every real source as missing
|
|
159
|
+
// (`sources-path-missing`) and refuse the stamp. `config.root` is the
|
|
160
|
+
// directory loam.json was found in, which is the only definition of
|
|
161
|
+
// "this repo" that does not move with the caller.
|
|
162
|
+
repoDir,
|
|
163
|
+
today: today(new Date()),
|
|
164
|
+
// Absent for an ordinary vouch, which is what makes an ordinary vouch
|
|
165
|
+
// CLEAR a prior sample's scope rather than leave it standing.
|
|
166
|
+
...(sample === undefined ? {} : { sample }),
|
|
167
|
+
});
|
|
168
|
+
if (!outcome.ok)
|
|
169
|
+
return fail(json, outcome.code, outcome.message);
|
|
170
|
+
const report = { service, docsDir: config.docsDir, outcome, ...(sample === undefined ? {} : { sample }) };
|
|
171
|
+
if (json)
|
|
172
|
+
return emitStampJson(report);
|
|
173
|
+
printStamp(report);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=vouch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vouch.js","sourceRoot":"","sources":["../../../src/commands/vouch/vouch.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig } from \"../../core/envelope/config.js\";\nimport { fail, NO_SERVICE_MESSAGE, reportNoConfig } from \"../../core/envelope/json.js\";\nimport { gitIdentity } from \"../../core/provenance/git.js\";\nimport { today } from \"../../core/provenance/stamp.js\";\nimport { serviceTreePathOf } from \"../../core/repo/service-target.js\";\nimport { runPack } from \"./pack/print.js\";\nimport { confirmVouch } from \"./prompt/confirm.js\";\nimport { emitStampJson, printStamp } from \"./report.js\";\nimport { vouch } from \"./run.js\";\nimport { buildSamplePlan, type SamplePlan } from \"./sample/plan.js\";\nimport { printReadingList } from \"./sample/print.js\";\n\n/**\n * `loam vouch` — the human act SCHEMA.md has always described as \"promote draft\n * -> verified\".\n *\n * Everything else loam checks is internal consistency, and an agent writing\n * fluent prose satisfies all of it. This is the one command that records a\n * person: they read the code, they say the document matches it, and loam stamps\n * a digest of what they read — and a digest of the document they read it\n * against, so neither side can move quietly. From then on `loam validate` can\n * tell the difference between a document nobody has checked, one that still\n * matches the code, one the code has moved out from under, and one whose own\n * prose changed after the stamp.\n *\n * \"Records a person\" was, for two releases, a thing this file said and did not\n * do. There was no `vouched_by`, no identity of any kind and no interactive\n * gate, so an agent could run it twice unattended and stamp `verified` both\n * times — while the skill files loam generates pre-approved `Bash(loam:*)`,\n * which meant the agent that wrote the draft was permitted to promote it. Three\n * things close that, and none of them is a signature: the stamp carries git's\n * `user.email` (`vouched_by`), the run refuses without a terminal or an explicit\n * `--yes`, and the generated allowlist names loam's read-only and authoring\n * verbs one by one instead of all of them (core/agent/tools/dialects.ts). What that buys is\n * attribution and a deliberate act, not proof — git config is a text file. A\n * reviewer can now ask a named person what they read, which is the question\n * `status: verified` was silently answering with nobody.\n *\n * The stamp is only worth what it claims, so vouch refuses everything it cannot\n * actually verify — a frontmatter block that will not parse (whose fields\n * nobody can read, and whose rewrite would lose the author's lines), a spec\n * with no sources, a glob pattern (no longer supported), a source that is\n * gone, a directory holding no files, or a repo that is not this service's —\n * and refuses without writing anything.\n *\n * \"The document\" is every spec-axis file the service has: spec.md always, and\n * arch.spec.md beside it when present — same frontmatter conventions, same\n * stamp, one vouch. The refusal discipline is per file but the vouch is\n * all-or-nothing per service: one unverifiable file refuses the whole run, so\n * a `verified` service never means \"half-stamped\".\n */\n\ninterface VouchOptions {\n service?: string;\n yes?: boolean;\n pack?: boolean;\n /** `--sample <n>` as typed — a string until it is proved to be a whole number. */\n sample?: string;\n json?: boolean;\n}\n\nexport function registerVouch(program: Command): void {\n program\n .command(\"vouch\")\n .description(\n \"Vouch for a service's living specs: stamp spec.md, and arch.spec.md when present, verified against the code they describe\",\n )\n .option(\"--service <id>\", \"service to vouch for (defaults to the configured service)\")\n .option(\n \"--yes\",\n \"skip the confirmation — required when stdin is not a terminal, and still records `vouched_by`\",\n )\n .option(\n \"--pack\",\n \"print the re-vouch reading pack — the doc-body diff since the last vouch, the source files that moved, and the sections already covered — then exit without vouching; read-only, nothing is stamped\",\n )\n .option(\n \"--sample <n>\",\n \"vouch after reading a deterministic sample of <n> sections per spec file — the stamp records `vouch_scope: sampled`, `loam list` shows `vouched (sampled)`, and `loam validate` reports `sources.sampled-vouch` until a full vouch clears it\",\n )\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (opts: VouchOptions) => {\n const json = opts.json === true;\n\n const loaded = await loadConfig();\n if (loaded.kind !== \"loaded\") {\n reportNoConfig(json, loaded);\n return;\n }\n const config = loaded.config;\n\n const service = opts.service ?? config.service;\n if (service === undefined) {\n return fail(json, \"invalid-option\", NO_SERVICE_MESSAGE);\n }\n // Vouching hashes the code the doc was written from, so it can only run\n // where that code is. From anywhere else the paths are someone else's.\n //\n // TWO conditions, two codes, and the split is the whole point. Both used\n // to answer `invalid-option`, whose `loam explain` row says the\n // invocation itself is wrong — contradictory flags, a mistyped one, a\n // value that cannot be right. Here the invocation is perfectly correct\n // and the REPOSITORY is the wrong one, so that code sent a user whose\n // flags were fine off to re-read their flags, and left CI and agents\n // unable to tell \"run this in the service repo\" from \"you typed the flag\n // wrong\". `loam verify --record --service` already draws exactly this\n // line for exactly this reason (see commands/verify/verify.ts); vouch\n // and gherkin now answer the same pair, so a caller branching on\n // `error.code` gets one answer from all three. The messages are\n // unchanged — only the code moved.\n if (config.service === undefined) {\n return fail(\n json,\n \"repository-unavailable\",\n `Cannot vouch for '${service}' from here: this is not a service repo. \\`sources\\` resolve against the service's own repo — run it there.`,\n );\n }\n if (config.service !== service) {\n return fail(\n json,\n \"service-mismatch\",\n `Cannot vouch for '${service}' from here: this repo is '${config.service}'. \\`sources\\` resolve against the service's own repo — run it there.`,\n );\n }\n\n // The repo root, not the cwd — see the comment on `repoDir` below; the\n // identity has to be asked of the same directory the sources resolve in,\n // or a subdirectory with its own git config would answer for it.\n const repoDir = config.root ?? process.cwd();\n\n // The flag's own grammar, before anything is read or hashed: `--sample`\n // decides what the run CLAIMS, so a value nobody can read must not reach\n // a stamp. Whole numbers only, and at least one — `0` is a vouch that\n // read nothing, and there is already a way to record that (do not\n // vouch). Checked before `--pack` below so a typo is refused the same\n // way in both modes.\n let sampleSize: number | undefined;\n if (opts.sample !== undefined) {\n if (!/^\\d+$/.test(opts.sample.trim()) || Number(opts.sample) < 1) {\n return fail(\n json,\n \"invalid-option\",\n `--sample expects a positive whole number of sections; got '${opts.sample}'. ` +\n \"Nothing was read and nothing was stamped.\",\n );\n }\n sampleSize = Number(opts.sample);\n }\n\n // The reading pack: read-only, so it runs BEFORE — and instead of —\n // every write-path gate below (identity, TTY, `--yes`, lock, journal),\n // which all defend the stamp; pack/pack.ts's banner carries the full\n // reasoning. It runs AFTER the wrong-repo gate above on purpose: the\n // source delta resolves `sources` here. `--yes` does not compose — it\n // is the unattended stamp, and a pack that stamped would defeat the\n // read it just prescribed.\n if (opts.pack === true) {\n if (opts.yes === true) {\n return fail(\n json,\n \"invalid-option\",\n \"--pack is the reading list and --yes is the unattended stamp; a pack that immediately \" +\n `stamped would defeat the read it just prescribed. Run --pack, read it, then \\`loam vouch --service ${service}\\`.`,\n );\n }\n // `--sample` composes with the pack instead of conflicting with it:\n // the pack is the reading list, and a sampled vouch's reading list is\n // the sample. Passing it here makes the pack print the sections the\n // subsequent `loam vouch --sample <n>` will stamp for — the same\n // derivation from the same seed, so the two cannot prescribe\n // different documents.\n return runPack({ docsDir: config.docsDir, service: config.service, repoDir, json, sample: sampleSize });\n }\n\n // Who. Before any reading, because a run that cannot name a person has\n // nothing to offer at the end of it and should not spend a digest finding\n // that out.\n const vouchedBy = await gitIdentity(repoDir);\n if (vouchedBy === null) {\n return fail(\n json,\n \"vouch-unattributable\",\n `Cannot vouch for '${service}': git names no \\`user.email\\` in ${repoDir}, so the stamp would ` +\n \"record a claim with nobody behind it — which is the one thing `status: verified` must not mean. \" +\n \"Set it (`git config user.email you@example.com`) and re-run.\",\n );\n }\n\n // Whether a person is actually here. `vouch` is the only command in loam\n // whose output is a claim about a HUMAN act — every other check is\n // internal consistency, which fluent prose satisfies — and it used to run\n // unattended, unattributed, twice in a row, stamping `verified` both\n // times. Meanwhile the generated skill files pre-approved `Bash(loam:*)`,\n // so the same agent that wrote the draft was permitted to promote it.\n // That inverts loam's own argument about test evidence: an agent must not\n // be able to SAY a scenario is tested, and it must not be able to say a\n // spec matches the code either. The allowlist no longer covers this\n // command (core/agent/tools/dialects.ts), and nothing but a terminal or an explicit\n // `--yes` gets past here.\n const attended = opts.yes === true;\n if (!attended) {\n if (process.stdin.isTTY !== true) {\n return fail(\n json,\n \"vouch-unattended\",\n `Cannot vouch for '${service}' with nothing on the other end of stdin: this command records that ` +\n \"a PERSON read the code and says the document matches it, and nobody was asked. Run it from a \" +\n \"terminal, or pass --yes to state that you are standing behind it anyway — `vouched_by` records \" +\n `${vouchedBy} either way.`,\n );\n }\n // `--json` and a prompt do not compose: the envelope is one JSON\n // document on stdout, and a question printed beside it is not parseable\n // by the consumer that asked for it. A JSON caller is a program, so it\n // gets the same answer a pipe does — say so with --yes.\n if (json) {\n return fail(\n json,\n \"vouch-unattended\",\n `Cannot vouch for '${service}' in --json mode without --yes: the confirmation is a question for a ` +\n \"person, and it cannot be asked on a stream whose whole contract is one JSON document.\",\n );\n }\n }\n\n // The reading list is built BEFORE the question and AFTER the attendance\n // gates: a run that cannot ask anybody anything must not spend two\n // source-tree digests finding that out, and a person must not be asked\n // to confirm a sample nobody has shown them. Every refusal a vouch can\n // raise about these documents fires in here, so it lands before the read\n // rather than after it.\n let sample: SamplePlan | undefined;\n if (sampleSize !== undefined) {\n const built = await buildSamplePlan({ docsDir: config.docsDir, service: config.service, repoDir, n: sampleSize });\n if (!built.ok) return fail(json, built.code, built.message);\n sample = built.plan;\n if (!json) printReadingList(sample, service);\n }\n const servicePath = await serviceTreePathOf(config.docsDir, service);\n if (!attended && !(await confirmVouch({ service, vouchedBy, docsDir: config.docsDir, servicePath, sample }))) {\n return fail(json, \"vouch-declined\", `Nothing was stamped for '${service}'.`);\n }\n\n const outcome = await vouch({\n docsDir: config.docsDir,\n // The guard above proved `service` IS `config.service`, and only the\n // latter carries the parse (`loam.json`'s `service` field is validated\n // at load). Same string, but only one spelling is pathable.\n service: config.service,\n vouchedBy,\n // The repo root, not the cwd. `sources:` are spelled relative to the\n // repository — that is what they mean in the frontmatter and what\n // `loam validate` resolves them against — so vouching from a\n // subdirectory used to report every real source as missing\n // (`sources-path-missing`) and refuse the stamp. `config.root` is the\n // directory loam.json was found in, which is the only definition of\n // \"this repo\" that does not move with the caller.\n repoDir,\n today: today(new Date()),\n // Absent for an ordinary vouch, which is what makes an ordinary vouch\n // CLEAR a prior sample's scope rather than leave it standing.\n ...(sample === undefined ? {} : { sample }),\n });\n if (!outcome.ok) return fail(json, outcome.code, outcome.message);\n\n const report = { service, docsDir: config.docsDir, outcome, ...(sample === undefined ? {} : { sample }) };\n if (json) return emitStampJson(report);\n printStamp(report);\n });\n}\n"]}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The document's opening arc: what the docs repo HOLDS — the layout, how its
|
|
3
|
+
* documents link to each other, the glossary, architectural obligations, and
|
|
4
|
+
* the `loam.json` wiring every repository commits.
|
|
5
|
+
*
|
|
6
|
+
* The three sections that used to close this file — the architecture spec
|
|
7
|
+
* axis, the generated Gherkin suite, and the frontmatter/vouch chain — are now
|
|
8
|
+
* the `loam-authoring` reference page (../workflows/reference/authoring.ts),
|
|
9
|
+
* printed by `loam instructions loam-authoring` rather than written into
|
|
10
|
+
* AGENTS.md. They are grammars consulted while writing ONE document, and the
|
|
11
|
+
* file they were in is auto-loaded on every session by hosts that truncate it
|
|
12
|
+
* silently; ./command-map.ts's header carries the measurement that decided it.
|
|
13
|
+
* What is left here is what an agent must know before it can form a question
|
|
14
|
+
* at all.
|
|
15
|
+
*
|
|
16
|
+
* One section of the AGENTS.md template. ../agents-md.ts assembles the
|
|
17
|
+
* document by PLAIN CONCATENATION — no join separator — so every section
|
|
18
|
+
* starts at the first character of its opening line and ends with the newline
|
|
19
|
+
* that closes its last one. Keep that shape when editing, or two sections glue
|
|
20
|
+
* onto one line in every docs repo loam scaffolds from now on.
|
|
21
|
+
*/
|
|
22
|
+
export const ARTIFACTS = `# Working in this docs repo
|
|
23
|
+
|
|
24
|
+
This is a **loam** docs repo: the shared source of truth for a governed software
|
|
25
|
+
system — its architecture, requirements, contracts and evidence. Everything here
|
|
26
|
+
is plain files. \`loam\` reads and writes them; delete \`loam\` and the docs remain.
|
|
27
|
+
|
|
28
|
+
**The bar this artifact set aims at is reproducibility**: a reader should be able
|
|
29
|
+
to answer, from these files alone — what each governed boundary exposes, what it
|
|
30
|
+
reaches, what shapes it exchanges, how it is run, and what pages whom —
|
|
31
|
+
without opening the code. \`loam validate\` grades form and joins, never depth:
|
|
32
|
+
green means the files agree with each other, and the bar is what "done" is
|
|
33
|
+
measured against — a thin baseline that validates is thin, not done.
|
|
34
|
+
|
|
35
|
+
## Layout
|
|
36
|
+
|
|
37
|
+
\`\`\`
|
|
38
|
+
architecture/landscape.likec4 the living C4 model of the whole system
|
|
39
|
+
architecture/permissions.yaml optional system-wide authorization vocabulary
|
|
40
|
+
architecture/obligations.yaml optional ARCHITECTURAL obligations (#obl-<name> on the map)
|
|
41
|
+
architecture/adrs/NNNN-*.md optional SYSTEM-level decisions (MADR) — only their links are graded
|
|
42
|
+
glossary/<term>.md optional domain vocabulary — one file per term, nesting allowed
|
|
43
|
+
services/<svc>/
|
|
44
|
+
model.likec4 this governed boundary's C4
|
|
45
|
+
spec.md its living requirements (current state)
|
|
46
|
+
arch.spec.md its living ARCHITECTURE requirements (outbox, retries, alerts)
|
|
47
|
+
openapi.yaml its API contract
|
|
48
|
+
asyncapi.yaml its AsyncAPI 3 event contract
|
|
49
|
+
adrs/ runbook.md health.yaml why it is like this, how to run it
|
|
50
|
+
features/<FEAT>/ a change in flight
|
|
51
|
+
intent.md why, in business terms
|
|
52
|
+
delta.likec4 the architecture change, tagged #<FEAT>
|
|
53
|
+
specs/<svc>/spec.md the requirement change for one governed boundary
|
|
54
|
+
specs/<svc>/arch.spec.md the architectural requirement change, same delta grammar
|
|
55
|
+
specs/<svc>/openapi.yaml the endpoints this feature adds
|
|
56
|
+
specs/<svc>/asyncapi.yaml the event-contract delta (optional; a complete AsyncAPI 3 document)
|
|
57
|
+
verification.yaml what was checked once the code was built
|
|
58
|
+
features/archive/<FEAT>/ shipped changes — the evolution history
|
|
59
|
+
\`\`\`
|
|
60
|
+
|
|
61
|
+
## Linking between documents
|
|
62
|
+
|
|
63
|
+
Documents here link to each other with **standard markdown links**, and the
|
|
64
|
+
target is a relative path to the file:
|
|
65
|
+
|
|
66
|
+
\`\`\`markdown
|
|
67
|
+
[0001 — transactional outbox](../../architecture/adrs/0001-transactional-outbox.md)
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
Two reasons, and the second is the one that decides it. A markdown link renders
|
|
71
|
+
as a link in pull-request review, which is where these documents are actually
|
|
72
|
+
read, while \`[[0001 transactional outbox]]\` renders there as the literal
|
|
73
|
+
brackets somebody typed. And its target is a real relative path, so "does this
|
|
74
|
+
link resolve" is a filesystem question with a yes-or-no answer — where resolving
|
|
75
|
+
a wikilink means reimplementing Obsidian's shortest-unique-path search across the
|
|
76
|
+
whole repo, which is guessing, and loam refuses and names rather than guessing.
|
|
77
|
+
|
|
78
|
+
You lose nothing by it. Obsidian reads a repo written this way with no
|
|
79
|
+
integration at all — graph view, backlinks and search all work on markdown links
|
|
80
|
+
— and its per-vault \`Use [[Wikilinks]]\` setting turns OFF, after which its own
|
|
81
|
+
autocomplete and rename-tracking produce markdown links too. The authoring
|
|
82
|
+
comfort is a toggle in somebody's editor, not a decision this repo carries.
|
|
83
|
+
|
|
84
|
+
**\`loam validate\` resolves these links** (\`link.unresolved\`, error): a relative
|
|
85
|
+
target that names nothing is a broken join, reported once per document with
|
|
86
|
+
every bad link and its line. A link here is a **join**, not decoration — a
|
|
87
|
+
requirement that names a term wants that term's document linked, and an ADR that
|
|
88
|
+
supersedes another wants to say which, the same kind of relationship
|
|
89
|
+
\`Operations:\` and \`Covers:\` already state.
|
|
90
|
+
|
|
91
|
+
Three things are deliberately NOT graded, so write them freely: a target outside
|
|
92
|
+
this repository (\`../../some-service/README.md\` — that tree may not be checked
|
|
93
|
+
out beside these documents), a link to a heading (\`#section\`), and any link
|
|
94
|
+
inside a fenced code block or an inline code span, which is how a document shows
|
|
95
|
+
the convention without being convicted for it.
|
|
96
|
+
|
|
97
|
+
CASE **is** graded, and deliberately: \`[Order](Order.md)\` beside a file called
|
|
98
|
+
\`order.md\` resolves on Windows and macOS and 404s on GitHub and every Linux
|
|
99
|
+
runner, so the message names the stored spelling on all of them alike.
|
|
100
|
+
|
|
101
|
+
## The glossary — the system's domain words
|
|
102
|
+
|
|
103
|
+
\`glossary/<term>.md\`, one file per term, nesting allowed
|
|
104
|
+
(\`glossary/payments/authorization.md\` is the term \`payments/authorization\`). There is
|
|
105
|
+
no \`glossary.yaml\`: a definition is prose, and the DIRECTORY is the list. The
|
|
106
|
+
directory's existence is the opt-in — a system without one hears nothing.
|
|
107
|
+
|
|
108
|
+
**A term is checkable because a link is a join.** Cite it from the document that
|
|
109
|
+
uses the word — a requirement, a capability document, a feature's intent — with an
|
|
110
|
+
ordinary relative link:
|
|
111
|
+
|
|
112
|
+
\`\`\`markdown
|
|
113
|
+
An [Order](../../glossary/order.md) is what a customer has confirmed.
|
|
114
|
+
\`\`\`
|
|
115
|
+
|
|
116
|
+
A citation that does not resolve is \`link.unresolved\` like any other; a term nothing
|
|
117
|
+
outside \`glossary/\` cites is \`glossary.unlinked\` (warn). Terms citing each other is
|
|
118
|
+
normal and does NOT count as adoption — a glossary is a network of definitions, and
|
|
119
|
+
two terms defining each other say nothing about whether the system uses either word.
|
|
120
|
+
\`loam list glossary\` prints every term with the documents that cite it.
|
|
121
|
+
|
|
122
|
+
**A feature may bring a new word with it**: write it at
|
|
123
|
+
\`features/<FEAT>/glossary/<term>.md\` and \`loam archive\` copies it into \`glossary/\`,
|
|
124
|
+
\`loam unarchive\` takes it back out. This route is CREATE-ONLY — a term the living
|
|
125
|
+
glossary already defines is \`glossary.term-exists\`, which \`--approve\` does not
|
|
126
|
+
override, because the merge is a whole-file copy and would replace an authored
|
|
127
|
+
definition wholesale. To CHANGE a definition, edit \`glossary/<term>.md\` directly in
|
|
128
|
+
the same pull request, where git produces an ordinary conflict.
|
|
129
|
+
|
|
130
|
+
Cite the new word at its FUTURE living path (\`../../../../glossary/order.md\` from a
|
|
131
|
+
\`specs/<svc>/\` delta) — that link resolves while the feature is in flight, and the
|
|
132
|
+
merge re-expresses it from wherever the text lands, so it keeps resolving afterwards.
|
|
133
|
+
|
|
134
|
+
Write the definition for a reader who knows the business and not this system: what the
|
|
135
|
+
word means, and what it is NOT (an order is not a cart). Never which service owns it —
|
|
136
|
+
that is the system map's question and it changes.
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
## Architectural obligations — what the architect hands the team
|
|
140
|
+
|
|
141
|
+
An architect has always had one checked channel here: an edge carrying
|
|
142
|
+
\`metadata { op 'authorizePayment' }\` obliges the provider to define that operationId,
|
|
143
|
+
or \`spine.op-undefined\` fails the gate. Obligations are the same handoff for the rules
|
|
144
|
+
that VARY — an outbox on this publisher and not that one, a circuit breaker on two
|
|
145
|
+
edges out of five — and they are three separate things on purpose:
|
|
146
|
+
|
|
147
|
+
- an **ADR** in \`architecture/adrs/\` says WHAT was decided, as thin or as thick as the
|
|
148
|
+
decision needs;
|
|
149
|
+
- a **\`#obl-<name>\` tag** on an element or an edge in \`architecture/landscape.likec4\`
|
|
150
|
+
says WHERE it applies, so one ADR governs three edges and not the fourth without the
|
|
151
|
+
document forking;
|
|
152
|
+
- **\`architecture/obligations.yaml\`** declares the names, so a mistyped tag is an error
|
|
153
|
+
rather than a word nobody notices.
|
|
154
|
+
|
|
155
|
+
\`\`\`yaml
|
|
156
|
+
obligations:
|
|
157
|
+
outbox:
|
|
158
|
+
description: Publishers write the event and the state change in one transaction.
|
|
159
|
+
adr: architecture/adrs/0001-transactional-outbox.md # relative to the docs repo root
|
|
160
|
+
\`\`\`
|
|
161
|
+
|
|
162
|
+
**Your side of it is \`Covers:\`**. A tagged object with no living requirement covering it
|
|
163
|
+
is \`obligation.uncovered\` (warn), and the finding names the service, the object and the
|
|
164
|
+
exact line to write — \`Covers: <element>\` or \`Covers: <source> -> <target>\` in that
|
|
165
|
+
service's \`arch.spec.md\`, with a scenario that proves it. An id is its own tag suffix,
|
|
166
|
+
so it may hold letters, digits, \`_\` and \`-\` only.
|
|
167
|
+
|
|
168
|
+
## \`loam.json\` — the wiring, in every repo
|
|
169
|
+
|
|
170
|
+
A governed system is **N + 1 repositories**: this docs repo, and one per governed
|
|
171
|
+
implementation boundary. Paths and commands call that boundary a \`service\`; it
|
|
172
|
+
may be a modular monolith, a network service, a CLI or a worker. Every repo
|
|
173
|
+
commits its own \`loam.json\`, so an agent, human or CI runner can find the rest
|
|
174
|
+
of the system. There is no global config; without a \`loam.json\`, commands refuse
|
|
175
|
+
with \`no-config\` rather than guessing.
|
|
176
|
+
|
|
177
|
+
\`\`\`json
|
|
178
|
+
{ "docsDir": "../docs-repo", "service": "payment-service", "gherkinDir": "features" }
|
|
179
|
+
\`\`\`
|
|
180
|
+
|
|
181
|
+
- \`docsDir\` — where the docs repo is, **stored exactly as it was passed** and
|
|
182
|
+
resolved against the directory holding the \`loam.json\`. So \`../docs-repo\`
|
|
183
|
+
keeps working on every machine that checks the repos out side by side, which
|
|
184
|
+
is the point of committing the file. An absolute path is a warning
|
|
185
|
+
(\`doctor.docs-absolute\`): it resolves only on the machine that ran \`loam init\`.
|
|
186
|
+
The docs repo's own \`loam.json\` says \`"docsDir": "."\`.
|
|
187
|
+
- \`service\` — the canonical id of the governed boundary THIS repo contains, i.e. the
|
|
188
|
+
\`services/<id>/\` directory it is allowed to speak for. \`loam vouch\`,
|
|
189
|
+
\`loam gherkin\` and \`loam verify --service\` when writing with \`--record\`,
|
|
190
|
+
\`--results\` or \`--contract-results\` bind to it; without it they refuse
|
|
191
|
+
(\`repository-unavailable\`) rather than write another boundary's documents
|
|
192
|
+
from a repo that is not that boundary's. A read-only verify checklist needs no
|
|
193
|
+
binding and works from the docs repo.
|
|
194
|
+
- \`gherkinDir\` — optional, default \`features\`: the directory \`loam gherkin\`
|
|
195
|
+
writes its own \`loam/\` subtree into.
|
|
196
|
+
- \`agentTools\` — optional: the agent tools \`loam init\` has written command and
|
|
197
|
+
skill files for here, accumulated across runs. Written by \`init\`, read by
|
|
198
|
+
\`loam doctor\`; nothing else depends on it, and a config without it loads.
|
|
199
|
+
|
|
200
|
+
**Step 0 of everything below, once per repo:**
|
|
201
|
+
|
|
202
|
+
\`\`\`sh
|
|
203
|
+
loam init --docs ../docs-repo --service payment-service # in EACH service repo
|
|
204
|
+
loam doctor # before you trust any of it
|
|
205
|
+
\`\`\`
|
|
206
|
+
|
|
207
|
+
\`loam init --docs <dir>\` **joins** an existing docs repo; creating a new one
|
|
208
|
+
takes \`--create\`, so a mistyped path cannot quietly scaffold a second source of
|
|
209
|
+
truth beside the real one. \`loam doctor\` is the read-only preflight: it reads
|
|
210
|
+
loam.json defensively, resolves \`docsDir\`, probes access, checks \`services/\` and
|
|
211
|
+
the system map, and reports the service binding — run it first when anything
|
|
212
|
+
behaves as though the system were empty.
|
|
213
|
+
|
|
214
|
+
**Living vs delta.** \`services/<svc>/spec.md\` is the complete current state.
|
|
215
|
+
\`features/<FEAT>/specs/<svc>/spec.md\` is a diff against it, reviewed as a diff, with
|
|
216
|
+
requirements grouped under \`## ADDED\`, \`## MODIFIED\` or \`## REMOVED Requirements\`.
|
|
217
|
+
\`loam archive\` folds the delta into the living state.
|
|
218
|
+
|
|
219
|
+
`;
|
|
220
|
+
//# sourceMappingURL=artifacts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../../../src/core/agent/agents-md/artifacts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqMxB,CAAC","sourcesContent":["/**\n * The document's opening arc: what the docs repo HOLDS — the layout, how its\n * documents link to each other, the glossary, architectural obligations, and\n * the `loam.json` wiring every repository commits.\n *\n * The three sections that used to close this file — the architecture spec\n * axis, the generated Gherkin suite, and the frontmatter/vouch chain — are now\n * the `loam-authoring` reference page (../workflows/reference/authoring.ts),\n * printed by `loam instructions loam-authoring` rather than written into\n * AGENTS.md. They are grammars consulted while writing ONE document, and the\n * file they were in is auto-loaded on every session by hosts that truncate it\n * silently; ./command-map.ts's header carries the measurement that decided it.\n * What is left here is what an agent must know before it can form a question\n * at all.\n *\n * One section of the AGENTS.md template. ../agents-md.ts assembles the\n * document by PLAIN CONCATENATION — no join separator — so every section\n * starts at the first character of its opening line and ends with the newline\n * that closes its last one. Keep that shape when editing, or two sections glue\n * onto one line in every docs repo loam scaffolds from now on.\n */\nexport const ARTIFACTS = `# Working in this docs repo\n\nThis is a **loam** docs repo: the shared source of truth for a governed software\nsystem — its architecture, requirements, contracts and evidence. Everything here\nis plain files. \\`loam\\` reads and writes them; delete \\`loam\\` and the docs remain.\n\n**The bar this artifact set aims at is reproducibility**: a reader should be able\nto answer, from these files alone — what each governed boundary exposes, what it\nreaches, what shapes it exchanges, how it is run, and what pages whom —\nwithout opening the code. \\`loam validate\\` grades form and joins, never depth:\ngreen means the files agree with each other, and the bar is what \"done\" is\nmeasured against — a thin baseline that validates is thin, not done.\n\n## Layout\n\n\\`\\`\\`\narchitecture/landscape.likec4 the living C4 model of the whole system\narchitecture/permissions.yaml optional system-wide authorization vocabulary\narchitecture/obligations.yaml optional ARCHITECTURAL obligations (#obl-<name> on the map)\narchitecture/adrs/NNNN-*.md optional SYSTEM-level decisions (MADR) — only their links are graded\nglossary/<term>.md optional domain vocabulary — one file per term, nesting allowed\nservices/<svc>/\n model.likec4 this governed boundary's C4\n spec.md its living requirements (current state)\n arch.spec.md its living ARCHITECTURE requirements (outbox, retries, alerts)\n openapi.yaml its API contract\n asyncapi.yaml its AsyncAPI 3 event contract\n adrs/ runbook.md health.yaml why it is like this, how to run it\nfeatures/<FEAT>/ a change in flight\n intent.md why, in business terms\n delta.likec4 the architecture change, tagged #<FEAT>\n specs/<svc>/spec.md the requirement change for one governed boundary\n specs/<svc>/arch.spec.md the architectural requirement change, same delta grammar\n specs/<svc>/openapi.yaml the endpoints this feature adds\n specs/<svc>/asyncapi.yaml the event-contract delta (optional; a complete AsyncAPI 3 document)\n verification.yaml what was checked once the code was built\nfeatures/archive/<FEAT>/ shipped changes — the evolution history\n\\`\\`\\`\n\n## Linking between documents\n\nDocuments here link to each other with **standard markdown links**, and the\ntarget is a relative path to the file:\n\n\\`\\`\\`markdown\n[0001 — transactional outbox](../../architecture/adrs/0001-transactional-outbox.md)\n\\`\\`\\`\n\nTwo reasons, and the second is the one that decides it. A markdown link renders\nas a link in pull-request review, which is where these documents are actually\nread, while \\`[[0001 transactional outbox]]\\` renders there as the literal\nbrackets somebody typed. And its target is a real relative path, so \"does this\nlink resolve\" is a filesystem question with a yes-or-no answer — where resolving\na wikilink means reimplementing Obsidian's shortest-unique-path search across the\nwhole repo, which is guessing, and loam refuses and names rather than guessing.\n\nYou lose nothing by it. Obsidian reads a repo written this way with no\nintegration at all — graph view, backlinks and search all work on markdown links\n— and its per-vault \\`Use [[Wikilinks]]\\` setting turns OFF, after which its own\nautocomplete and rename-tracking produce markdown links too. The authoring\ncomfort is a toggle in somebody's editor, not a decision this repo carries.\n\n**\\`loam validate\\` resolves these links** (\\`link.unresolved\\`, error): a relative\ntarget that names nothing is a broken join, reported once per document with\nevery bad link and its line. A link here is a **join**, not decoration — a\nrequirement that names a term wants that term's document linked, and an ADR that\nsupersedes another wants to say which, the same kind of relationship\n\\`Operations:\\` and \\`Covers:\\` already state.\n\nThree things are deliberately NOT graded, so write them freely: a target outside\nthis repository (\\`../../some-service/README.md\\` — that tree may not be checked\nout beside these documents), a link to a heading (\\`#section\\`), and any link\ninside a fenced code block or an inline code span, which is how a document shows\nthe convention without being convicted for it.\n\nCASE **is** graded, and deliberately: \\`[Order](Order.md)\\` beside a file called\n\\`order.md\\` resolves on Windows and macOS and 404s on GitHub and every Linux\nrunner, so the message names the stored spelling on all of them alike.\n\n## The glossary — the system's domain words\n\n\\`glossary/<term>.md\\`, one file per term, nesting allowed\n(\\`glossary/payments/authorization.md\\` is the term \\`payments/authorization\\`). There is\nno \\`glossary.yaml\\`: a definition is prose, and the DIRECTORY is the list. The\ndirectory's existence is the opt-in — a system without one hears nothing.\n\n**A term is checkable because a link is a join.** Cite it from the document that\nuses the word — a requirement, a capability document, a feature's intent — with an\nordinary relative link:\n\n\\`\\`\\`markdown\nAn [Order](../../glossary/order.md) is what a customer has confirmed.\n\\`\\`\\`\n\nA citation that does not resolve is \\`link.unresolved\\` like any other; a term nothing\noutside \\`glossary/\\` cites is \\`glossary.unlinked\\` (warn). Terms citing each other is\nnormal and does NOT count as adoption — a glossary is a network of definitions, and\ntwo terms defining each other say nothing about whether the system uses either word.\n\\`loam list glossary\\` prints every term with the documents that cite it.\n\n**A feature may bring a new word with it**: write it at\n\\`features/<FEAT>/glossary/<term>.md\\` and \\`loam archive\\` copies it into \\`glossary/\\`,\n\\`loam unarchive\\` takes it back out. This route is CREATE-ONLY — a term the living\nglossary already defines is \\`glossary.term-exists\\`, which \\`--approve\\` does not\noverride, because the merge is a whole-file copy and would replace an authored\ndefinition wholesale. To CHANGE a definition, edit \\`glossary/<term>.md\\` directly in\nthe same pull request, where git produces an ordinary conflict.\n\nCite the new word at its FUTURE living path (\\`../../../../glossary/order.md\\` from a\n\\`specs/<svc>/\\` delta) — that link resolves while the feature is in flight, and the\nmerge re-expresses it from wherever the text lands, so it keeps resolving afterwards.\n\nWrite the definition for a reader who knows the business and not this system: what the\nword means, and what it is NOT (an order is not a cart). Never which service owns it —\nthat is the system map's question and it changes.\n\n\n## Architectural obligations — what the architect hands the team\n\nAn architect has always had one checked channel here: an edge carrying\n\\`metadata { op 'authorizePayment' }\\` obliges the provider to define that operationId,\nor \\`spine.op-undefined\\` fails the gate. Obligations are the same handoff for the rules\nthat VARY — an outbox on this publisher and not that one, a circuit breaker on two\nedges out of five — and they are three separate things on purpose:\n\n- an **ADR** in \\`architecture/adrs/\\` says WHAT was decided, as thin or as thick as the\n decision needs;\n- a **\\`#obl-<name>\\` tag** on an element or an edge in \\`architecture/landscape.likec4\\`\n says WHERE it applies, so one ADR governs three edges and not the fourth without the\n document forking;\n- **\\`architecture/obligations.yaml\\`** declares the names, so a mistyped tag is an error\n rather than a word nobody notices.\n\n\\`\\`\\`yaml\nobligations:\n outbox:\n description: Publishers write the event and the state change in one transaction.\n adr: architecture/adrs/0001-transactional-outbox.md # relative to the docs repo root\n\\`\\`\\`\n\n**Your side of it is \\`Covers:\\`**. A tagged object with no living requirement covering it\nis \\`obligation.uncovered\\` (warn), and the finding names the service, the object and the\nexact line to write — \\`Covers: <element>\\` or \\`Covers: <source> -> <target>\\` in that\nservice's \\`arch.spec.md\\`, with a scenario that proves it. An id is its own tag suffix,\nso it may hold letters, digits, \\`_\\` and \\`-\\` only.\n\n## \\`loam.json\\` — the wiring, in every repo\n\nA governed system is **N + 1 repositories**: this docs repo, and one per governed\nimplementation boundary. Paths and commands call that boundary a \\`service\\`; it\nmay be a modular monolith, a network service, a CLI or a worker. Every repo\ncommits its own \\`loam.json\\`, so an agent, human or CI runner can find the rest\nof the system. There is no global config; without a \\`loam.json\\`, commands refuse\nwith \\`no-config\\` rather than guessing.\n\n\\`\\`\\`json\n{ \"docsDir\": \"../docs-repo\", \"service\": \"payment-service\", \"gherkinDir\": \"features\" }\n\\`\\`\\`\n\n- \\`docsDir\\` — where the docs repo is, **stored exactly as it was passed** and\n resolved against the directory holding the \\`loam.json\\`. So \\`../docs-repo\\`\n keeps working on every machine that checks the repos out side by side, which\n is the point of committing the file. An absolute path is a warning\n (\\`doctor.docs-absolute\\`): it resolves only on the machine that ran \\`loam init\\`.\n The docs repo's own \\`loam.json\\` says \\`\"docsDir\": \".\"\\`.\n- \\`service\\` — the canonical id of the governed boundary THIS repo contains, i.e. the\n \\`services/<id>/\\` directory it is allowed to speak for. \\`loam vouch\\`,\n \\`loam gherkin\\` and \\`loam verify --service\\` when writing with \\`--record\\`,\n \\`--results\\` or \\`--contract-results\\` bind to it; without it they refuse\n (\\`repository-unavailable\\`) rather than write another boundary's documents\n from a repo that is not that boundary's. A read-only verify checklist needs no\n binding and works from the docs repo.\n- \\`gherkinDir\\` — optional, default \\`features\\`: the directory \\`loam gherkin\\`\n writes its own \\`loam/\\` subtree into.\n- \\`agentTools\\` — optional: the agent tools \\`loam init\\` has written command and\n skill files for here, accumulated across runs. Written by \\`init\\`, read by\n \\`loam doctor\\`; nothing else depends on it, and a config without it loads.\n\n**Step 0 of everything below, once per repo:**\n\n\\`\\`\\`sh\nloam init --docs ../docs-repo --service payment-service # in EACH service repo\nloam doctor # before you trust any of it\n\\`\\`\\`\n\n\\`loam init --docs <dir>\\` **joins** an existing docs repo; creating a new one\ntakes \\`--create\\`, so a mistyped path cannot quietly scaffold a second source of\ntruth beside the real one. \\`loam doctor\\` is the read-only preflight: it reads\nloam.json defensively, resolves \\`docsDir\\`, probes access, checks \\`services/\\` and\nthe system map, and reports the service binding — run it first when anything\nbehaves as though the system were empty.\n\n**Living vs delta.** \\`services/<svc>/spec.md\\` is the complete current state.\n\\`features/<FEAT>/specs/<svc>/spec.md\\` is a diff against it, reviewed as a diff, with\nrequirements grouped under \\`## ADDED\\`, \\`## MODIFIED\\` or \\`## REMOVED Requirements\\`.\n\\`loam archive\\` folds the delta into the living state.\n\n`;\n"]}
|