@ybotok/loam 0.1.0-beta.3 → 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 +2548 -86
- package/COMPARISON.md +80 -63
- package/CONTRIBUTING.md +84 -0
- package/MIGRATING-from-OpenSpec.md +213 -54
- package/QUICKSTART.md +125 -0
- package/README.md +450 -150
- package/ROADMAP.md +494 -0
- package/SCHEMA.md +2355 -256
- package/SECURITY.md +21 -3
- package/WORKFLOW.md +701 -0
- package/dist/cli.js +113 -19
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +209 -0
- package/dist/commands/adopt/adopt.js.map +1 -0
- package/dist/commands/adopt/render.js +268 -0
- package/dist/commands/adopt/render.js.map +1 -0
- package/dist/commands/archive/archive.js +77 -0
- package/dist/commands/archive/archive.js.map +1 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +159 -0
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +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 +72 -14
- package/dist/commands/instructions.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +65 -0
- package/dist/commands/list/campaign/campaign.js.map +1 -0
- package/dist/commands/list/campaign/owners.js +65 -0
- package/dist/commands/list/campaign/owners.js.map +1 -0
- package/dist/commands/list/json.js +157 -0
- package/dist/commands/list/json.js.map +1 -0
- package/dist/commands/list/list.js +343 -0
- package/dist/commands/list/list.js.map +1 -0
- package/dist/commands/list/print.js +301 -0
- package/dist/commands/list/print.js.map +1 -0
- package/dist/commands/list/review.js +120 -0
- package/dist/commands/list/review.js.map +1 -0
- package/dist/commands/list/views.js +105 -0
- package/dist/commands/list/views.js.map +1 -0
- package/dist/commands/mcp/dispatch.js +183 -0
- package/dist/commands/mcp/dispatch.js.map +1 -0
- package/dist/commands/mcp/mcp.js +34 -0
- package/dist/commands/mcp/mcp.js.map +1 -0
- package/dist/commands/mcp/serve.js +55 -0
- package/dist/commands/mcp/serve.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/active.js +170 -0
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
- package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/living.js +62 -0
- package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/target.js +219 -0
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/error.js +19 -0
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/read.js +49 -0
- package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
- package/dist/commands/migrate-openspec/print.js +89 -0
- package/dist/commands/migrate-openspec/print.js.map +1 -0
- package/dist/commands/new/commit.js +76 -0
- package/dist/commands/new/commit.js.map +1 -0
- package/dist/commands/new/new.js +283 -0
- package/dist/commands/new/new.js.map +1 -0
- package/dist/commands/new/templates.js +355 -0
- package/dist/commands/new/templates.js.map +1 -0
- package/dist/commands/open.js +203 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/policy/format.js +152 -0
- package/dist/commands/policy/format.js.map +1 -0
- package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
- package/dist/commands/policy/gate.js.map +1 -0
- package/dist/commands/rebase/edit.js +106 -0
- package/dist/commands/rebase/edit.js.map +1 -0
- package/dist/commands/rebase/living.js +185 -0
- package/dist/commands/rebase/living.js.map +1 -0
- package/dist/commands/rebase/plan.js +245 -0
- package/dist/commands/rebase/plan.js.map +1 -0
- package/dist/commands/rebase/rebase.js +275 -0
- package/dist/commands/rebase/rebase.js.map +1 -0
- package/dist/commands/seed/commit.js +192 -0
- package/dist/commands/seed/commit.js.map +1 -0
- package/dist/commands/seed/plan.js +133 -0
- package/dist/commands/seed/plan.js.map +1 -0
- package/dist/commands/seed/seed.js +172 -0
- package/dist/commands/seed/seed.js.map +1 -0
- package/dist/commands/show/feature.js +178 -0
- package/dist/commands/show/feature.js.map +1 -0
- package/dist/commands/show/marks.js +24 -0
- package/dist/commands/show/marks.js.map +1 -0
- package/dist/commands/{show.js → show/service.js} +63 -152
- package/dist/commands/show/service.js.map +1 -0
- package/dist/commands/show/show.js +77 -0
- package/dist/commands/show/show.js.map +1 -0
- package/dist/commands/status/json.js +45 -0
- package/dist/commands/status/json.js.map +1 -0
- package/dist/commands/status/print.js +157 -0
- package/dist/commands/status/print.js.map +1 -0
- package/dist/commands/status/status.js +119 -0
- package/dist/commands/status/status.js.map +1 -0
- package/dist/commands/steps/steps.js +189 -0
- package/dist/commands/steps/steps.js.map +1 -0
- package/dist/commands/subsystem/edit.js +192 -0
- package/dist/commands/subsystem/edit.js.map +1 -0
- package/dist/commands/subsystem/move.js +265 -0
- package/dist/commands/subsystem/move.js.map +1 -0
- package/dist/commands/subsystem/query.js +115 -0
- package/dist/commands/subsystem/query.js.map +1 -0
- package/dist/commands/subsystem/subsystem.js +101 -0
- package/dist/commands/subsystem/subsystem.js.map +1 -0
- package/dist/commands/subsystem/sync.js +103 -0
- package/dist/commands/subsystem/sync.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +94 -0
- package/dist/commands/subsystem/txn/txn.js.map +1 -0
- package/dist/commands/subsystem/txn/views.js +70 -0
- package/dist/commands/subsystem/txn/views.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +160 -0
- package/dist/commands/unarchive/manifest.js.map +1 -0
- package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
- package/dist/commands/unarchive/restore.js.map +1 -0
- package/dist/commands/unarchive/unarchive.js +67 -0
- package/dist/commands/unarchive/unarchive.js.map +1 -0
- package/dist/commands/validate/arch-coverage.js +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/{agents-stamp.js → agent/agents-stamp.js} +50 -0
- package/dist/core/agent/agents-stamp.js.map +1 -0
- package/dist/core/agent/contract.js +2 -0
- package/dist/core/agent/contract.js.map +1 -0
- package/dist/core/agent/protocol.js +193 -0
- package/dist/core/agent/protocol.js.map +1 -0
- package/dist/core/agent/scaffold.js +259 -0
- package/dist/core/agent/scaffold.js.map +1 -0
- package/dist/core/agent/support/report.js +103 -0
- package/dist/core/agent/support/report.js.map +1 -0
- package/dist/core/agent/tools/dialects.js +150 -0
- package/dist/core/agent/tools/dialects.js.map +1 -0
- package/dist/core/agent/tools/registry.js +228 -0
- package/dist/core/agent/tools/registry.js.map +1 -0
- package/dist/core/agent/workflows/adopt.js +133 -0
- package/dist/core/agent/workflows/adopt.js.map +1 -0
- package/dist/core/agent/workflows/check.js +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.js → asyncapi/read.js} +95 -109
- package/dist/core/asyncapi/read.js.map +1 -0
- package/dist/core/brief/brief.js +225 -0
- package/dist/core/brief/brief.js.map +1 -0
- package/dist/core/brief/checks.js +250 -0
- package/dist/core/brief/checks.js.map +1 -0
- package/dist/core/brief/landscape.js +194 -0
- package/dist/core/brief/landscape.js.map +1 -0
- package/dist/core/brief/targets.js +226 -0
- package/dist/core/brief/targets.js.map +1 -0
- package/dist/core/brief/unchecked.js +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 +7 -64
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +222 -0
- package/dist/core/doctor/agents.js.map +1 -0
- package/dist/core/doctor/config.js +135 -0
- package/dist/core/doctor/config.js.map +1 -0
- package/dist/core/doctor/doctor.js +275 -0
- package/dist/core/doctor/doctor.js.map +1 -0
- package/dist/core/doctor/report.js +13 -0
- package/dist/core/doctor/report.js.map +1 -0
- package/dist/core/doctor/residue.js +110 -0
- package/dist/core/doctor/residue.js.map +1 -0
- package/dist/core/document/apply.js +77 -0
- package/dist/core/document/apply.js.map +1 -0
- package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
- package/dist/core/document/frontmatter.js.map +1 -0
- package/dist/core/document/grammar/directives.js +143 -0
- package/dist/core/document/grammar/directives.js.map +1 -0
- package/dist/core/document/parse.js +263 -0
- package/dist/core/document/parse.js.map +1 -0
- package/dist/core/document/scenarios.js +221 -0
- package/dist/core/document/scenarios.js.map +1 -0
- package/dist/core/document/spec.js +241 -0
- package/dist/core/document/spec.js.map +1 -0
- package/dist/core/{config.js → envelope/config.js} +79 -18
- package/dist/core/envelope/config.js.map +1 -0
- package/dist/core/envelope/json.js +175 -0
- package/dist/core/envelope/json.js.map +1 -0
- package/dist/core/{version.js → envelope/version.js} +3 -3
- package/dist/core/envelope/version.js.map +1 -0
- package/dist/core/explain/families.js +175 -0
- package/dist/core/explain/families.js.map +1 -0
- package/dist/core/explain/fix-tables.js +178 -0
- package/dist/core/explain/fix-tables.js.map +1 -0
- package/dist/core/explain/lookup.js +181 -0
- package/dist/core/explain/lookup.js.map +1 -0
- package/dist/core/explain/refusals.js +49 -0
- package/dist/core/explain/refusals.js.map +1 -0
- package/dist/core/explain/terms.js +144 -0
- package/dist/core/explain/terms.js.map +1 -0
- package/dist/core/explore/describe.js +105 -0
- package/dist/core/explore/describe.js.map +1 -0
- package/dist/core/{explore.js → explore/explore.js} +50 -101
- package/dist/core/explore/explore.js.map +1 -0
- package/dist/core/fleet-context.js +120 -78
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +201 -0
- package/dist/core/gate/checks.js.map +1 -0
- package/dist/core/gate/partners.js +205 -0
- package/dist/core/gate/partners.js.map +1 -0
- package/dist/core/gate/report.js +47 -0
- package/dist/core/gate/report.js.map +1 -0
- package/dist/core/gate/verification.js +113 -0
- package/dist/core/gate/verification.js.map +1 -0
- package/dist/core/gherkin/digest.js +60 -0
- package/dist/core/gherkin/digest.js.map +1 -0
- package/dist/core/gherkin/emit.js +177 -0
- package/dist/core/gherkin/emit.js.map +1 -0
- package/dist/core/gherkin/read.js +76 -0
- package/dist/core/gherkin/read.js.map +1 -0
- package/dist/core/gherkin/stale.js +234 -0
- package/dist/core/gherkin/stale.js.map +1 -0
- package/dist/core/gherkin/stamp.js +77 -0
- package/dist/core/gherkin/stamp.js.map +1 -0
- package/dist/core/gherkin/steps/catalogue.js +154 -0
- package/dist/core/gherkin/steps/catalogue.js.map +1 -0
- package/dist/core/gherkin/steps/inventory.js +98 -0
- package/dist/core/gherkin/steps/inventory.js.map +1 -0
- package/dist/core/gherkin/steps/phrase.js +72 -0
- package/dist/core/gherkin/steps/phrase.js.map +1 -0
- package/dist/core/glossary/delta.js +106 -0
- package/dist/core/glossary/delta.js.map +1 -0
- package/dist/core/glossary/tree.js +86 -0
- package/dist/core/glossary/tree.js.map +1 -0
- package/dist/core/kernel/concurrency.js.map +1 -0
- package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
- package/dist/core/kernel/document-bytes.js.map +1 -0
- package/dist/core/kernel/fences.js +42 -0
- package/dist/core/kernel/fences.js.map +1 -0
- package/dist/core/kernel/ids/capability.js +72 -0
- package/dist/core/kernel/ids/capability.js.map +1 -0
- package/dist/core/kernel/ids/dirs.js +33 -0
- package/dist/core/kernel/ids/dirs.js.map +1 -0
- package/dist/core/kernel/ids/feature.js +75 -0
- package/dist/core/kernel/ids/feature.js.map +1 -0
- package/dist/core/kernel/ids/service.js +156 -0
- package/dist/core/kernel/ids/service.js.map +1 -0
- package/dist/core/kernel/ids/subsystem.js +63 -0
- package/dist/core/kernel/ids/subsystem.js.map +1 -0
- package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
- package/dist/core/kernel/path-safety.js.map +1 -0
- package/dist/core/kernel/records.js.map +1 -0
- package/dist/core/links/backlinks.js +68 -0
- package/dist/core/links/backlinks.js.map +1 -0
- package/dist/core/links/case.js +74 -0
- package/dist/core/links/case.js.map +1 -0
- package/dist/core/links/corpus.js +104 -0
- package/dist/core/links/corpus.js.map +1 -0
- package/dist/core/links/findings.js +110 -0
- package/dist/core/links/findings.js.map +1 -0
- package/dist/core/links/parse.js +236 -0
- package/dist/core/links/parse.js.map +1 -0
- package/dist/core/mcp/argv.js +111 -0
- package/dist/core/mcp/argv.js.map +1 -0
- package/dist/core/mcp/author-tools.js +72 -0
- package/dist/core/mcp/author-tools.js.map +1 -0
- package/dist/core/mcp/framing.js +46 -0
- package/dist/core/mcp/framing.js.map +1 -0
- package/dist/core/mcp/protocol.js +280 -0
- package/dist/core/mcp/protocol.js.map +1 -0
- package/dist/core/mcp/tools.js +314 -0
- package/dist/core/mcp/tools.js.map +1 -0
- package/dist/core/obligations/obligations.js +106 -0
- package/dist/core/obligations/obligations.js.map +1 -0
- package/dist/core/openapi/baseline/gate.js +166 -0
- package/dist/core/openapi/baseline/gate.js.map +1 -0
- package/dist/core/openapi/baseline/plan.js +112 -0
- package/dist/core/openapi/baseline/plan.js.map +1 -0
- package/dist/core/openapi/baseline/record.js +175 -0
- package/dist/core/openapi/baseline/record.js.map +1 -0
- package/dist/core/openapi/depth.js +122 -0
- package/dist/core/openapi/depth.js.map +1 -0
- package/dist/core/openapi/digest.js +189 -0
- package/dist/core/openapi/digest.js.map +1 -0
- package/dist/core/{openapi.js → openapi/doc.js} +44 -106
- package/dist/core/openapi/doc.js.map +1 -0
- package/dist/core/openapi/merge/components.js +234 -0
- package/dist/core/openapi/merge/components.js.map +1 -0
- package/dist/core/openapi/merge/error.js +26 -0
- package/dist/core/openapi/merge/error.js.map +1 -0
- package/dist/core/openapi/merge/markers.js +208 -0
- package/dist/core/openapi/merge/markers.js.map +1 -0
- package/dist/core/openapi/merge/merge.js +256 -0
- package/dist/core/openapi/merge/merge.js.map +1 -0
- package/dist/core/openapi/merge/pin.js +128 -0
- package/dist/core/openapi/merge/pin.js.map +1 -0
- package/dist/core/openspec/decide/capabilities.js +244 -0
- package/dist/core/openspec/decide/capabilities.js.map +1 -0
- package/dist/core/openspec/decide/dispositions.js +63 -0
- package/dist/core/openspec/decide/dispositions.js.map +1 -0
- package/dist/core/openspec/decide/renames.js +179 -0
- package/dist/core/openspec/decide/renames.js.map +1 -0
- package/dist/core/openspec/decide/verdict.js +84 -0
- package/dist/core/openspec/decide/verdict.js.map +1 -0
- package/dist/core/openspec/inventory.js +221 -0
- package/dist/core/openspec/inventory.js.map +1 -0
- package/dist/core/openspec/model/mapping.js +76 -0
- package/dist/core/openspec/model/mapping.js.map +1 -0
- package/dist/core/openspec/model/model.js +65 -0
- package/dist/core/openspec/model/model.js.map +1 -0
- package/dist/core/openspec/scan/changes.js +235 -0
- package/dist/core/openspec/scan/changes.js.map +1 -0
- package/dist/core/openspec/scan/config.js +200 -0
- package/dist/core/openspec/scan/config.js.map +1 -0
- package/dist/core/openspec/scan/shape.js +260 -0
- package/dist/core/openspec/scan/shape.js.map +1 -0
- package/dist/core/openspec/scan/walk.js +133 -0
- package/dist/core/openspec/scan/walk.js.map +1 -0
- package/dist/core/openspec/scan/workspace.js +71 -0
- package/dist/core/openspec/scan/workspace.js.map +1 -0
- package/dist/core/owners/codeowners.js +141 -0
- package/dist/core/owners/codeowners.js.map +1 -0
- package/dist/core/pack/features.js +120 -0
- package/dist/core/pack/features.js.map +1 -0
- package/dist/core/pack/joins.js +112 -0
- package/dist/core/pack/joins.js.map +1 -0
- package/dist/core/pack/living.js +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 -295
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/migrate-openspec.js +0 -950
- package/dist/commands/migrate-openspec.js.map +0 -1
- package/dist/commands/new.js +0 -413
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/rebase.js +0 -356
- package/dist/commands/rebase.js.map +0 -1
- package/dist/commands/show.js.map +0 -1
- package/dist/commands/status.js +0 -262
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/unarchive.js.map +0 -1
- package/dist/commands/validate.js +0 -1773
- package/dist/commands/validate.js.map +0 -1
- package/dist/commands/verify.js +0 -775
- package/dist/commands/verify.js.map +0 -1
- package/dist/commands/vouch.js +0 -421
- package/dist/commands/vouch.js.map +0 -1
- package/dist/core/agent.js +0 -2173
- package/dist/core/agent.js.map +0 -1
- package/dist/core/agents-stamp.js.map +0 -1
- package/dist/core/arch.js +0 -130
- package/dist/core/arch.js.map +0 -1
- package/dist/core/asyncapi.js.map +0 -1
- package/dist/core/brief.js +0 -755
- package/dist/core/brief.js.map +0 -1
- package/dist/core/coherence.js +0 -577
- package/dist/core/coherence.js.map +0 -1
- package/dist/core/concurrency.js.map +0 -1
- package/dist/core/config.js.map +0 -1
- package/dist/core/delta.js +0 -476
- package/dist/core/delta.js.map +0 -1
- package/dist/core/dependencies.js +0 -356
- package/dist/core/dependencies.js.map +0 -1
- package/dist/core/doctor.js +0 -596
- package/dist/core/doctor.js.map +0 -1
- package/dist/core/document-bytes.js.map +0 -1
- package/dist/core/explore.js.map +0 -1
- package/dist/core/frontmatter.js.map +0 -1
- package/dist/core/gherkin.js +0 -463
- package/dist/core/gherkin.js.map +0 -1
- package/dist/core/health.js +0 -67
- package/dist/core/health.js.map +0 -1
- package/dist/core/ids.js +0 -122
- package/dist/core/ids.js.map +0 -1
- package/dist/core/issue.js +0 -9
- package/dist/core/issue.js.map +0 -1
- package/dist/core/json.js +0 -74
- package/dist/core/json.js.map +0 -1
- package/dist/core/likec4.js +0 -489
- package/dist/core/likec4.js.map +0 -1
- package/dist/core/maturity.js +0 -66
- package/dist/core/maturity.js.map +0 -1
- package/dist/core/openapi-merge.js +0 -552
- package/dist/core/openapi-merge.js.map +0 -1
- package/dist/core/openapi.js.map +0 -1
- package/dist/core/openspec-inventory.js +0 -1440
- package/dist/core/openspec-inventory.js.map +0 -1
- package/dist/core/path-safety.js.map +0 -1
- package/dist/core/provenance.js +0 -787
- package/dist/core/provenance.js.map +0 -1
- package/dist/core/records.js.map +0 -1
- package/dist/core/repo.js +0 -438
- package/dist/core/repo.js.map +0 -1
- package/dist/core/report.js.map +0 -1
- package/dist/core/spec.js +0 -505
- package/dist/core/spec.js.map +0 -1
- package/dist/core/staging.js +0 -890
- package/dist/core/staging.js.map +0 -1
- package/dist/core/status.js +0 -1132
- package/dist/core/status.js.map +0 -1
- package/dist/core/steps.js +0 -72
- package/dist/core/steps.js.map +0 -1
- package/dist/core/verify.js +0 -807
- package/dist/core/verify.js.map +0 -1
- package/dist/core/version.js.map +0 -1
- /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
- /package/dist/core/{records.js → kernel/records.js} +0 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The explain lookup: one subject in — a finding code, an envelope refusal
|
|
3
|
+
* code, or a concept term — one discriminated explanation out. It also answers
|
|
4
|
+
* the two listings the command prints with no subject: `listTerms` for the
|
|
5
|
+
* concept vocabulary and `listCodes` for the code vocabulary, the latter built
|
|
6
|
+
* from the same `findingIn` the per-code answer uses so the inventory cannot
|
|
7
|
+
* describe a code differently from the page about it.
|
|
8
|
+
*
|
|
9
|
+
* Assembled ON DEMAND, never cached at module level: tests run in forked
|
|
10
|
+
* processes that chdir per invocation, and a long-running host (the MCP
|
|
11
|
+
* server) outlives many calls — a table built at import time is exactly the
|
|
12
|
+
* module-level mutable state AGENTS.md names as a hazard. The build is a walk
|
|
13
|
+
* over constants already in memory, so there is nothing worth caching anyway.
|
|
14
|
+
*
|
|
15
|
+
* Resolution order — finding tables first, then the family registry, then
|
|
16
|
+
* refusal codes, then terms — IS the tie-break: a subject in two families
|
|
17
|
+
* would silently take the earlier family's answer (a refusal named like a term
|
|
18
|
+
* alias would win over the term). No naming convention keeps them apart —
|
|
19
|
+
* `based-on` is a dashed term alias, exactly the shape a refusal code takes —
|
|
20
|
+
* so test/explain.test.ts asserts the key sets are pairwise disjoint, and the
|
|
21
|
+
* order never actually decides anything.
|
|
22
|
+
*
|
|
23
|
+
* The one place that order is load-bearing anyway is the fix tables against
|
|
24
|
+
* ./families.ts: the tables are PARSED from the bytes the binary ships and the
|
|
25
|
+
* registry is written by hand, so where both could answer, the parsed prose has
|
|
26
|
+
* to win and the hand-written row must never silently shadow it. It is written
|
|
27
|
+
* first here and asserted disjoint there, which makes the precedence a fact
|
|
28
|
+
* about the code as well as a fact about the data.
|
|
29
|
+
*/
|
|
30
|
+
import { COMMANDS } from "../agent/protocol.js";
|
|
31
|
+
import { VALIDATE_CHECKS } from "../brief/checks.js";
|
|
32
|
+
import { familyCodes, familyFinding } from "./families.js";
|
|
33
|
+
import { parseFixRows } from "./fix-tables.js";
|
|
34
|
+
import { REFUSAL_MEANINGS } from "./refusals.js";
|
|
35
|
+
import { TERMS } from "./terms.js";
|
|
36
|
+
/** Every fix-table row shipped in the workflow bodies, in protocol order. */
|
|
37
|
+
function allRows() {
|
|
38
|
+
return COMMANDS.flatMap((command) => parseFixRows(command.body));
|
|
39
|
+
}
|
|
40
|
+
function briefFor(subject) {
|
|
41
|
+
return VALIDATE_CHECKS.find((check) => check.code === subject);
|
|
42
|
+
}
|
|
43
|
+
/** Terms match case-insensitively, by name or alias — codes stay exact, they are already lowercase by grammar. */
|
|
44
|
+
function termFor(subject) {
|
|
45
|
+
const wanted = subject.toLowerCase();
|
|
46
|
+
return TERMS.find((entry) => entry.term === wanted || entry.aliases.includes(wanted));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The finding answer for one subject out of rows ALREADY parsed, or null when
|
|
50
|
+
* no table grades it.
|
|
51
|
+
*
|
|
52
|
+
* Split out of `explainSubject` so `listCodes` builds its rows through the
|
|
53
|
+
* very same function rather than a parallel one: the inventory's per-code
|
|
54
|
+
* payload is then the per-code answer by construction, not by two pieces of
|
|
55
|
+
* code agreeing. It also keeps the inventory's cost linear — `explainSubject`
|
|
56
|
+
* re-parses every workflow body on each call, and calling it 270 times to
|
|
57
|
+
* enumerate the vocabulary would re-walk the whole corpus 270 times.
|
|
58
|
+
*/
|
|
59
|
+
function findingIn(rows, subject) {
|
|
60
|
+
const brief = briefFor(subject);
|
|
61
|
+
// VALIDATE_CHECKS contributes `via` ONLY, never a fallback entry. An
|
|
62
|
+
// earlier draft synthesized a row from the brief when the tables had none,
|
|
63
|
+
// and that fallback was both unreachable (every brief check has a table
|
|
64
|
+
// row, pinned by test/explain.test.ts) and wrong if ever reached: its
|
|
65
|
+
// scope would have been the brief's invocation string, a spelling no
|
|
66
|
+
// parsed row uses. A brief check that LOSES its table row should fail
|
|
67
|
+
// loudly there — explain refusing is the tripwire — not render a
|
|
68
|
+
// one-of-a-kind page nobody else prints.
|
|
69
|
+
const entries = rows
|
|
70
|
+
.filter((row) => row.codes.includes(subject))
|
|
71
|
+
.map(({ scope, severityNote, meaning, fix }) => ({ scope, severityNote, meaning, fix }));
|
|
72
|
+
if (entries.length === 0)
|
|
73
|
+
return null;
|
|
74
|
+
return { kind: "finding", entries, ...(brief === undefined ? {} : { via: brief.via }) };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The finding answer from EITHER source, tables first.
|
|
78
|
+
*
|
|
79
|
+
* One function rather than two call sites choosing an order, because the order
|
|
80
|
+
* is the precedence rule: a code a fix table grades is answered from the bytes
|
|
81
|
+
* the binary ships, and ./families.ts only ever answers codes no table reaches.
|
|
82
|
+
* `via` is a fix-table-only fact — it comes from a `VALIDATE_CHECKS` entry, and
|
|
83
|
+
* no brief check names a `doctor.*`, `next.*`, `diff.*` or `gate.*` code — so a
|
|
84
|
+
* registry answer carries a single entry and nothing else, which is the same
|
|
85
|
+
* shape a one-table code already returns.
|
|
86
|
+
*/
|
|
87
|
+
function findingFor(rows, subject) {
|
|
88
|
+
const table = findingIn(rows, subject);
|
|
89
|
+
if (table !== null)
|
|
90
|
+
return table;
|
|
91
|
+
const family = familyFinding(subject);
|
|
92
|
+
return family === null ? null : { kind: "finding", entries: [family] };
|
|
93
|
+
}
|
|
94
|
+
export function explainSubject(subject) {
|
|
95
|
+
const finding = findingFor(allRows(), subject);
|
|
96
|
+
if (finding !== null)
|
|
97
|
+
return finding;
|
|
98
|
+
// Through Object.entries rather than an indexed read: the record's keys are
|
|
99
|
+
// `ErrorCode`, `subject` is an arbitrary string, and the Map lookup needs
|
|
100
|
+
// neither a key cast nor a hasOwn pre-check.
|
|
101
|
+
const refusal = new Map(Object.entries(REFUSAL_MEANINGS)).get(subject);
|
|
102
|
+
if (refusal !== undefined)
|
|
103
|
+
return { kind: "refusal", meaning: refusal };
|
|
104
|
+
const term = termFor(subject);
|
|
105
|
+
if (term !== undefined)
|
|
106
|
+
return { kind: "term", term: term.term, paragraph: term.paragraph };
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Every subject `explainSubject` can answer — for the unknown-subject
|
|
111
|
+
* refusal's close-match suggestions. Sorted, so suggestion ties break the
|
|
112
|
+
* same way on every machine.
|
|
113
|
+
*/
|
|
114
|
+
export function knownSubjects() {
|
|
115
|
+
const known = new Set();
|
|
116
|
+
for (const row of allRows())
|
|
117
|
+
for (const rowCode of row.codes)
|
|
118
|
+
known.add(rowCode);
|
|
119
|
+
for (const familyCode of familyCodes())
|
|
120
|
+
known.add(familyCode);
|
|
121
|
+
for (const check of VALIDATE_CHECKS)
|
|
122
|
+
known.add(check.code);
|
|
123
|
+
for (const key of Object.keys(REFUSAL_MEANINGS))
|
|
124
|
+
known.add(key);
|
|
125
|
+
for (const entry of TERMS) {
|
|
126
|
+
known.add(entry.term);
|
|
127
|
+
for (const alias of entry.aliases)
|
|
128
|
+
known.add(alias);
|
|
129
|
+
}
|
|
130
|
+
return [...known].sort();
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* A paragraph's first sentence — the one-line summary form both listings
|
|
134
|
+
* print. One definition rather than two: the term listing and the code
|
|
135
|
+
* listing must cut prose the same way, or the same sentence reads as two
|
|
136
|
+
* different summaries depending on which command printed it.
|
|
137
|
+
*/
|
|
138
|
+
export function firstSentence(text) {
|
|
139
|
+
const cut = text.indexOf(". ");
|
|
140
|
+
return cut === -1 ? text : text.slice(0, cut + 1);
|
|
141
|
+
}
|
|
142
|
+
/** The no-argument listing: each term with its paragraph's first sentence as the one-line summary. */
|
|
143
|
+
export function listTerms() {
|
|
144
|
+
return TERMS.map(({ term, paragraph }) => ({ term, summary: firstSentence(paragraph) }));
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Every code `explainSubject` can answer — the machine-readable inventory of
|
|
148
|
+
* the vocabulary, which is otherwise discoverable only by parsing the 83 KB
|
|
149
|
+
* prose body of `loam instructions loam-check` or by reading TypeScript
|
|
150
|
+
* unions that are erased at runtime.
|
|
151
|
+
*
|
|
152
|
+
* Sorted within each kind rather than left in document order: the fix tables
|
|
153
|
+
* are ordered by scope for somebody working through one table, but an
|
|
154
|
+
* inventory is read by name and diffed between binaries, and document order
|
|
155
|
+
* would reshuffle on an unrelated table edit.
|
|
156
|
+
*/
|
|
157
|
+
export function listCodes() {
|
|
158
|
+
const rows = allRows();
|
|
159
|
+
// One sorted finding group over BOTH sources rather than a third listing
|
|
160
|
+
// heading. A caller building a code-to-fix cache asks "what does this binary
|
|
161
|
+
// explain", and a `doctor.*` row that arrived from ./families.ts rather than
|
|
162
|
+
// from a parsed table is the same answer to that question — the row it gets
|
|
163
|
+
// is `explain <code> --json`'s payload either way. Splitting them would put
|
|
164
|
+
// the provenance of the prose into the machine contract, where nothing
|
|
165
|
+
// branches on it.
|
|
166
|
+
const findings = [...new Set([...rows.flatMap((row) => row.codes), ...familyCodes()])]
|
|
167
|
+
.sort()
|
|
168
|
+
// Non-null: the code came out of `rows` or out of the registry, and
|
|
169
|
+
// `findingFor` reads exactly those two.
|
|
170
|
+
.map((code) => ({ code, ...findingFor(rows, code) }));
|
|
171
|
+
// Through a Map for the same reason `explainSubject` reads one: the record's
|
|
172
|
+
// keys are `ErrorCode` and this listing's are plain strings. Plain `.sort()`
|
|
173
|
+
// throughout, never `localeCompare` — the order is part of the machine
|
|
174
|
+
// contract and must not depend on the host's ICU locale.
|
|
175
|
+
const meanings = new Map(Object.entries(REFUSAL_MEANINGS));
|
|
176
|
+
const refusals = [...meanings.keys()]
|
|
177
|
+
.sort()
|
|
178
|
+
.map((code) => ({ code, kind: "refusal", meaning: meanings.get(code) }));
|
|
179
|
+
return [...findings, ...refusals];
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=lookup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookup.js","sourceRoot":"","sources":["../../../src/core/explain/lookup.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The explain lookup: one subject in — a finding code, an envelope refusal\n * code, or a concept term — one discriminated explanation out. It also answers\n * the two listings the command prints with no subject: `listTerms` for the\n * concept vocabulary and `listCodes` for the code vocabulary, the latter built\n * from the same `findingIn` the per-code answer uses so the inventory cannot\n * describe a code differently from the page about it.\n *\n * Assembled ON DEMAND, never cached at module level: tests run in forked\n * processes that chdir per invocation, and a long-running host (the MCP\n * server) outlives many calls — a table built at import time is exactly the\n * module-level mutable state AGENTS.md names as a hazard. The build is a walk\n * over constants already in memory, so there is nothing worth caching anyway.\n *\n * Resolution order — finding tables first, then the family registry, then\n * refusal codes, then terms — IS the tie-break: a subject in two families\n * would silently take the earlier family's answer (a refusal named like a term\n * alias would win over the term). No naming convention keeps them apart —\n * `based-on` is a dashed term alias, exactly the shape a refusal code takes —\n * so test/explain.test.ts asserts the key sets are pairwise disjoint, and the\n * order never actually decides anything.\n *\n * The one place that order is load-bearing anyway is the fix tables against\n * ./families.ts: the tables are PARSED from the bytes the binary ships and the\n * registry is written by hand, so where both could answer, the parsed prose has\n * to win and the hand-written row must never silently shadow it. It is written\n * first here and asserted disjoint there, which makes the precedence a fact\n * about the code as well as a fact about the data.\n */\nimport { COMMANDS } from \"../agent/protocol.js\";\nimport { VALIDATE_CHECKS, type BriefCheck } from \"../brief/checks.js\";\nimport { familyCodes, familyFinding } from \"./families.js\";\nimport { parseFixRows, type FixRow } from \"./fix-tables.js\";\nimport { REFUSAL_MEANINGS } from \"./refusals.js\";\nimport { TERMS, type TermEntry } from \"./terms.js\";\n\nexport interface FindingEntry {\n /** Which table graded it — `--service <id>`, `--feature <FEAT-id>`, `--all`, `loam archive`, … */\n readonly scope: string;\n /** The table's verbatim parenthetical, or \"error\" — its default. */\n readonly severityNote: string;\n readonly meaning: string;\n readonly fix: string;\n}\n\nexport type Explanation =\n | { readonly kind: \"finding\"; readonly entries: readonly FindingEntry[]; readonly via?: string }\n | { readonly kind: \"refusal\"; readonly meaning: string }\n | { readonly kind: \"term\"; readonly term: string; readonly paragraph: string };\n\n/** Every fix-table row shipped in the workflow bodies, in protocol order. */\nfunction allRows(): FixRow[] {\n return COMMANDS.flatMap((command) => parseFixRows(command.body));\n}\n\nfunction briefFor(subject: string): BriefCheck | undefined {\n return VALIDATE_CHECKS.find((check) => check.code === subject);\n}\n\n/** Terms match case-insensitively, by name or alias — codes stay exact, they are already lowercase by grammar. */\nfunction termFor(subject: string): TermEntry | undefined {\n const wanted = subject.toLowerCase();\n return TERMS.find((entry) => entry.term === wanted || entry.aliases.includes(wanted));\n}\n\n/**\n * The finding answer for one subject out of rows ALREADY parsed, or null when\n * no table grades it.\n *\n * Split out of `explainSubject` so `listCodes` builds its rows through the\n * very same function rather than a parallel one: the inventory's per-code\n * payload is then the per-code answer by construction, not by two pieces of\n * code agreeing. It also keeps the inventory's cost linear — `explainSubject`\n * re-parses every workflow body on each call, and calling it 270 times to\n * enumerate the vocabulary would re-walk the whole corpus 270 times.\n */\nfunction findingIn(rows: readonly FixRow[], subject: string): Extract<Explanation, { kind: \"finding\" }> | null {\n const brief = briefFor(subject);\n // VALIDATE_CHECKS contributes `via` ONLY, never a fallback entry. An\n // earlier draft synthesized a row from the brief when the tables had none,\n // and that fallback was both unreachable (every brief check has a table\n // row, pinned by test/explain.test.ts) and wrong if ever reached: its\n // scope would have been the brief's invocation string, a spelling no\n // parsed row uses. A brief check that LOSES its table row should fail\n // loudly there — explain refusing is the tripwire — not render a\n // one-of-a-kind page nobody else prints.\n const entries: FindingEntry[] = rows\n .filter((row) => row.codes.includes(subject))\n .map(({ scope, severityNote, meaning, fix }) => ({ scope, severityNote, meaning, fix }));\n if (entries.length === 0) return null;\n return { kind: \"finding\", entries, ...(brief === undefined ? {} : { via: brief.via }) };\n}\n\n/**\n * The finding answer from EITHER source, tables first.\n *\n * One function rather than two call sites choosing an order, because the order\n * is the precedence rule: a code a fix table grades is answered from the bytes\n * the binary ships, and ./families.ts only ever answers codes no table reaches.\n * `via` is a fix-table-only fact — it comes from a `VALIDATE_CHECKS` entry, and\n * no brief check names a `doctor.*`, `next.*`, `diff.*` or `gate.*` code — so a\n * registry answer carries a single entry and nothing else, which is the same\n * shape a one-table code already returns.\n */\nfunction findingFor(rows: readonly FixRow[], subject: string): Extract<Explanation, { kind: \"finding\" }> | null {\n const table = findingIn(rows, subject);\n if (table !== null) return table;\n const family = familyFinding(subject);\n return family === null ? null : { kind: \"finding\", entries: [family] };\n}\n\nexport function explainSubject(subject: string): Explanation | null {\n const finding = findingFor(allRows(), subject);\n if (finding !== null) return finding;\n // Through Object.entries rather than an indexed read: the record's keys are\n // `ErrorCode`, `subject` is an arbitrary string, and the Map lookup needs\n // neither a key cast nor a hasOwn pre-check.\n const refusal = new Map<string, string>(Object.entries(REFUSAL_MEANINGS)).get(subject);\n if (refusal !== undefined) return { kind: \"refusal\", meaning: refusal };\n const term = termFor(subject);\n if (term !== undefined) return { kind: \"term\", term: term.term, paragraph: term.paragraph };\n return null;\n}\n\n/**\n * Every subject `explainSubject` can answer — for the unknown-subject\n * refusal's close-match suggestions. Sorted, so suggestion ties break the\n * same way on every machine.\n */\nexport function knownSubjects(): string[] {\n const known = new Set<string>();\n for (const row of allRows()) for (const rowCode of row.codes) known.add(rowCode);\n for (const familyCode of familyCodes()) known.add(familyCode);\n for (const check of VALIDATE_CHECKS) known.add(check.code);\n for (const key of Object.keys(REFUSAL_MEANINGS)) known.add(key);\n for (const entry of TERMS) {\n known.add(entry.term);\n for (const alias of entry.aliases) known.add(alias);\n }\n return [...known].sort();\n}\n\n/**\n * A paragraph's first sentence — the one-line summary form both listings\n * print. One definition rather than two: the term listing and the code\n * listing must cut prose the same way, or the same sentence reads as two\n * different summaries depending on which command printed it.\n */\nexport function firstSentence(text: string): string {\n const cut = text.indexOf(\". \");\n return cut === -1 ? text : text.slice(0, cut + 1);\n}\n\n/** The no-argument listing: each term with its paragraph's first sentence as the one-line summary. */\nexport function listTerms(): Array<{ term: string; summary: string }> {\n return TERMS.map(({ term, paragraph }) => ({ term, summary: firstSentence(paragraph) }));\n}\n\n/**\n * One row of the code inventory: the per-code answer with its code attached.\n *\n * DERIVED from `Explanation` rather than restated, so `loam explain <code>\n * --json` and `loam explain --codes --json` cannot describe the same code with\n * two different key sets — a caching consumer reads one and looks up the other.\n * The `term` arm is excluded on purpose: a concept term is a different kind of\n * subject, and the bare `loam explain` listing keeps them.\n *\n * There is deliberately NO `severity` or `gates` key here, and adding one\n * would be a defect rather than an improvement. A fix table carries only the\n * verbatim parenthetical its author wrote (`warn, gates archive; one per\n * service`), which is prose about a scope, while the answer a caller would\n * actually branch on is computed per ISSUE at runtime by `gatesArchive()` and\n * `approveOverrides()` in core/vocabulary/issue.ts — from `severity`, an\n * optional per-issue `gates`, and a never-overridable code set. A severity\n * derived from the parenthetical here would be a second answer to a question\n * that already has one, free to disagree with the binary's own gate.\n */\nexport type CodeListing = { readonly code: string } & Exclude<Explanation, { kind: \"term\" }>;\n\n/**\n * Every code `explainSubject` can answer — the machine-readable inventory of\n * the vocabulary, which is otherwise discoverable only by parsing the 83 KB\n * prose body of `loam instructions loam-check` or by reading TypeScript\n * unions that are erased at runtime.\n *\n * Sorted within each kind rather than left in document order: the fix tables\n * are ordered by scope for somebody working through one table, but an\n * inventory is read by name and diffed between binaries, and document order\n * would reshuffle on an unrelated table edit.\n */\nexport function listCodes(): CodeListing[] {\n const rows = allRows();\n // One sorted finding group over BOTH sources rather than a third listing\n // heading. A caller building a code-to-fix cache asks \"what does this binary\n // explain\", and a `doctor.*` row that arrived from ./families.ts rather than\n // from a parsed table is the same answer to that question — the row it gets\n // is `explain <code> --json`'s payload either way. Splitting them would put\n // the provenance of the prose into the machine contract, where nothing\n // branches on it.\n const findings: CodeListing[] = [...new Set([...rows.flatMap((row) => row.codes), ...familyCodes()])]\n .sort()\n // Non-null: the code came out of `rows` or out of the registry, and\n // `findingFor` reads exactly those two.\n .map((code) => ({ code, ...findingFor(rows, code)! }));\n // Through a Map for the same reason `explainSubject` reads one: the record's\n // keys are `ErrorCode` and this listing's are plain strings. Plain `.sort()`\n // throughout, never `localeCompare` — the order is part of the machine\n // contract and must not depend on the host's ICU locale.\n const meanings = new Map<string, string>(Object.entries(REFUSAL_MEANINGS));\n const refusals: CodeListing[] = [...meanings.keys()]\n .sort()\n .map((code) => ({ code, kind: \"refusal\" as const, meaning: meanings.get(code)! }));\n return [...findings, ...refusals];\n}\n"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export const REFUSAL_MEANINGS = {
|
|
2
|
+
"no-config": "No loam.json at or above the working directory — the repo is not wired to a fleet; run `loam init --docs <dir>` once per repository rather than letting a command guess where the fleet lives.",
|
|
3
|
+
"config-invalid": "loam.json exists but cannot be loaded — repair the file (or delete it and re-run `loam init`); the code is distinct from `no-config` because running init over a corrupt config would silently rewrite it.",
|
|
4
|
+
"unknown-target": "The name resolves to nothing this command can address — no such service, feature, workflow or subject; the message offers the closest known names.",
|
|
5
|
+
"invalid-option": "The invocation itself is wrong — flags that contradict each other, a value that cannot be right, or a mistyped flag or command; nothing was read or written, and with `--json` anywhere in the arguments even commander's own usage errors arrive as this envelope.",
|
|
6
|
+
"already-exists": "Refusing to create over something that already exists — a feature directory, a workspace file, a name already claimed in the subsystem namespace — because overwriting would destroy work nobody asked to lose.",
|
|
7
|
+
"sources-absent": "`loam vouch` refuses to stamp a document that names no `sources` — with nothing tying the document to code, the staleness signal a vouch exists to power could never fire.",
|
|
8
|
+
"sources-path-missing": "`loam vouch` refuses to stamp: a listed source does not exist in the service repo, or is a glob pattern (not supported) — the digest would cover a different file set than the author meant.",
|
|
9
|
+
"not-coherent": "`loam archive` refuses: the feature has coherence issues, carried in `issues[]` with a per-issue `overridable` — fix the gating ones, or `--approve` the judgment calls; a refusal `--approve` cannot move says so in the message.",
|
|
10
|
+
"living-outside-requirements": "`loam archive` refuses: the LIVING spec holds a requirement outside its `## Requirements` section, and the merge rewrites only that section, so the requirement would land in the file twice — the fix is in the living document, and `--approve` does not override it.",
|
|
11
|
+
"archive-exists": "`loam archive` refuses: `features/archive/` already holds a directory for this feature — a destination collision, not a merge problem; move the old archive entry aside first.",
|
|
12
|
+
"merge-failed": "The merge — or a journaled writer's one-file commit — could not be computed or was rolled back; the living docs are unchanged, and re-running can work.",
|
|
13
|
+
"rollback-incomplete": "A failed write could not be fully undone: some files were not put back, and the message lists them — the one refusal that needs a human before anything else runs against the repo.",
|
|
14
|
+
"feature-active": "`loam unarchive` refuses: a `features/` directory for this feature already exists outside the archive, so restoring would write over work in flight — remove or rename it, then re-run.",
|
|
15
|
+
"snapshot-missing": "`loam unarchive` refuses: the archive carries no snapshot of what it overwrote (it predates snapshots, or was written by a different loam) — restore the living docs from version control instead.",
|
|
16
|
+
"snapshot-stale": "`loam unarchive` refuses: the living docs changed after the archive, so restoring is no longer an undo — re-run with `--force` to restore anyway, deliberately discarding those later changes.",
|
|
17
|
+
"snapshot-corrupt": "`loam unarchive` refuses: a snapshot pre-image no longer matches the digest the archive recorded for it, so restoring would write text nobody wrote — `--force` does not override this one; restore from version control.",
|
|
18
|
+
"commit-interrupted": "A journaled writer (archive, unarchive, rebase, vouch, new, gherkin, a recording verify) was killed mid-commit and this run cannot repair the leftovers on its own — the docs may be half-written; run the command the message names, or `loam doctor` for the file-by-file account.",
|
|
19
|
+
"restore-failed": "`loam unarchive` failed and everything was rolled back — nothing was restored, the living docs are unchanged, and re-running can work.",
|
|
20
|
+
"answers-unreadable": "A recording `loam verify` could not read a user-named file — the answers JSON, or a cucumber/contract report — as what it claims to be; fail-closed: nothing was recorded, so fix the file and re-run.",
|
|
21
|
+
"answers-mismatch": "The answer set does not answer the current checklist — an id not on it, a claim with no answer, or an entry for a claim a passed report already owns; re-derive the checklist and answer exactly what it asks.",
|
|
22
|
+
"answers-unevidenced": "A `confirmed` answer carries no evidence — evidence is `file:line`, and a claim you cannot show is `unconfirmed` with a note saying why, which is a successful record, not a failure.",
|
|
23
|
+
"service-mismatch": "A command that must run inside one service's own repository was told `--service` for a service this repository's loam.json does not declare: a recording `loam verify`, whose attestation must be bound to the code it is about; `loam gherkin`, whose .feature files land in that service's checkout; or `loam vouch`, whose `sources` resolve there. Run it from that service's own repo.",
|
|
24
|
+
"unknown-service": "Federated verify recording: the selected service is not one this feature's checklist (or the docs repo) knows, so there is nothing its attestation could bind to — fix the id.",
|
|
25
|
+
"repository-unavailable": "The repository this operation must be bound to (or read from) cannot be answered for — most commonly a writing `loam verify`, a `loam gherkin` or a `loam vouch` run where loam.json declares no `service` at all (the docs repo, usually), or a docs repo that cannot be enumerated — so the command refuses rather than guessing which service it is about.",
|
|
26
|
+
"record-federated": "`loam verify --record` without `--service` over a federated (schema 2) record — the all-at-once form would erase other repositories' attestations, so it is refused naming the attestors; record per service instead.",
|
|
27
|
+
"record-unreadable": "A verification.yaml exists but cannot be read as a record — it is never overwritten and never reported as absent; repair it by hand, or re-record from the service's repo.",
|
|
28
|
+
"record-raced": "`loam verify --record` found the record changed between its locked read and the swap — an editor or a lock-ignoring writer landed first; nothing was written, and re-running merges over the record as it now stands.",
|
|
29
|
+
"gherkin-conflict": "`loam gherkin <FEAT>` would overwrite a `.feature` file owned by another feature still in flight — the whole emission refuses and names the owner; archive or coordinate with that feature first.",
|
|
30
|
+
"vouch-raced": "`loam vouch` found the spec changed between reading and stamping — another vouch or an edit landed first; nothing was written, so re-read and re-run.",
|
|
31
|
+
"vouch-unattended": "`loam vouch` with nobody on the other end of stdin and no `--yes` — the stamp is a person's claim to have read the document, and nobody was asked.",
|
|
32
|
+
"vouch-unattributable": "`loam vouch` could not learn who is vouching — git names no `user.email` here — and a stamp with nobody behind it records only that the word was written; set your git identity and re-run.",
|
|
33
|
+
"vouch-declined": "`loam vouch` asked and the person said no — nothing was stamped; a successful refusal, not a failure.",
|
|
34
|
+
"docs-missing": "`docsDir` in loam.json points at nothing — the docs repo was never cloned, or the path is wrong; a read command refuses rather than reporting an empty fleet, and `loam doctor` names the wiring to fix.",
|
|
35
|
+
"services-missing": "`docsDir` is a directory but holds no `services/` — it is some other directory, most often the service repo itself after a typo; fix the pointer rather than scaffolding a second source of truth beside the real one.",
|
|
36
|
+
"docs-busy": "Another loam writer holds the docs repo's advisory lock — nothing was read or written, and re-running once it finishes works; a lock nothing will ever release shows up in `loam doctor` as a blocker.",
|
|
37
|
+
"subsystem-not-empty": "`loam subsystem` rm refuses a subsystem that still holds members — services or child subsystems, named in the message — because a destructive command never picks targets the caller did not name; move them out first, then re-run.",
|
|
38
|
+
"move-uncommitted": "A subsystem move/rename refuses because git reports uncommitted or untracked paths under a directory being moved — the rename would sweep them into a move nobody reviewed; commit, `git stash -u`, or remove them, then re-run.",
|
|
39
|
+
"move-failed": "A subsystem move/rename failed and was rolled back cleanly — every rename undone, the generated views file restored, the docs unchanged; re-running can work.",
|
|
40
|
+
"no-members": "`loam open` found no service checkout bound to this docs repo under any scanned root — the workspace would hold only the docs repo; clone a bound checkout beside it, or point `--root` at where the checkouts live.",
|
|
41
|
+
"binding-duplicate": "Two discovered repositories' committed loam.json files declare the same `service` for this docs repo — loam will not guess which checkout speaks for the service; narrow the scan with `--root`, or fix the stray binding.",
|
|
42
|
+
"owners-unreadable": "`loam list --owners` could not use the named CODEOWNERS file — the path cannot be read, or a line does not parse as `pattern owner…` (the message names the line); fail-closed, because a half-read ownership file must never file a service under the wrong team.",
|
|
43
|
+
"seed-file-invalid": "`loam seed`'s fleet file is missing, unreadable, not YAML, the wrong shape, carries an illegal id — or no longer names every existing `services/<id>/` (that refusal lists the exact ids as `missingServices`); the message names the file and line, and editing the file and re-running succeeds.",
|
|
44
|
+
"seed-duplicate-service": "fleet.yaml declares one name twice — as two services, or as both a service and an external/subsystem; service ids, subsystem names and externals share one flat namespace, both declaration lines are named, and renaming one fixes it.",
|
|
45
|
+
"seed-unknown-subsystem": "A service's `subsystem:` in fleet.yaml names nothing `subsystems:` declares — the message carries a did-you-mean over the names the file really declares; add the subsystem or fix the spelling, then re-run.",
|
|
46
|
+
"seed-landscape-edited": "architecture/landscape.likec4 carries hand edits (the line-1 stamp's digest no longer matches) or was authored some other way — `loam seed` never overwrites human work and wrote nothing; fold the edits into fleet.yaml and delete the file, or keep the hand-authored map and stop using seed.",
|
|
47
|
+
internal: "An unexpected throw — the one refusal with no stable meaning; it exists so a `--json` consumer still receives an envelope instead of a stack trace, and a repeatable `internal` is a loam defect worth reporting.",
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=refusals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refusals.js","sourceRoot":"","sources":["../../../src/core/explain/refusals.ts"],"names":[],"mappings":"AAiBA,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,WAAW,EACT,gMAAgM;IAClM,gBAAgB,EACd,4MAA4M;IAC9M,gBAAgB,EACd,oJAAoJ;IACtJ,gBAAgB,EACd,qQAAqQ;IACvQ,gBAAgB,EACd,iNAAiN;IACnN,gBAAgB,EACd,4KAA4K;IAC9K,sBAAsB,EACpB,8LAA8L;IAChM,cAAc,EACZ,oOAAoO;IACtO,6BAA6B,EAC3B,yQAAyQ;IAC3Q,gBAAgB,EACd,gLAAgL;IAClL,cAAc,EACZ,yJAAyJ;IAC3J,qBAAqB,EACnB,qLAAqL;IACvL,gBAAgB,EACd,yLAAyL;IAC3L,kBAAkB,EAChB,oMAAoM;IACtM,gBAAgB,EACd,gMAAgM;IAClM,kBAAkB,EAChB,2NAA2N;IAC7N,oBAAoB,EAClB,sRAAsR;IACxR,gBAAgB,EACd,wIAAwI;IAC1I,oBAAoB,EAClB,wMAAwM;IAC1M,kBAAkB,EAChB,gNAAgN;IAClN,qBAAqB,EACnB,uLAAuL;IACzL,kBAAkB,EAChB,6XAA6X;IAC/X,iBAAiB,EACf,gLAAgL;IAClL,wBAAwB,EACtB,+VAA+V;IACjW,kBAAkB,EAChB,uNAAuN;IACzN,mBAAmB,EACjB,4KAA4K;IAC9K,cAAc,EACZ,uNAAuN;IACzN,kBAAkB,EAChB,mMAAmM;IACrM,aAAa,EACX,uJAAuJ;IACzJ,kBAAkB,EAChB,oJAAoJ;IACtJ,sBAAsB,EACpB,6LAA6L;IAC/L,gBAAgB,EACd,uGAAuG;IACzG,cAAc,EACZ,0MAA0M;IAC5M,kBAAkB,EAChB,wNAAwN;IAC1N,WAAW,EACT,wMAAwM;IAC1M,qBAAqB,EACnB,sOAAsO;IACxO,kBAAkB,EAChB,kOAAkO;IACpO,aAAa,EACX,+JAA+J;IACjK,YAAY,EACV,sNAAsN;IACxN,mBAAmB,EACjB,4NAA4N;IAC9N,mBAAmB,EACjB,oQAAoQ;IACtQ,mBAAmB,EACjB,oSAAoS;IACtS,wBAAwB,EACtB,yOAAyO;IAC3O,wBAAwB,EACtB,+MAA+M;IACjN,uBAAuB,EACrB,mSAAmS;IACrS,QAAQ,EACN,mNAAmN;CACzK,CAAC","sourcesContent":["/**\n * One sentence per envelope refusal code, for `loam explain`.\n *\n * This is the one deliberate restatement in the explain lookup, and the\n * compiler is its drift test: unlike the finding codes — whose prose ships in\n * the /loam-check fix tables and is PARSED at runtime (fix-tables.ts) — the\n * `ErrorCode` union's meanings live only in doc comments, which are erased,\n * so there is no runtime constant to read. `satisfies Record<ErrorCode,\n * string>` makes the pairing exhaustive both ways: a new union member cannot\n * ship without a row here (missing key fails typecheck), and a row for a\n * retired code fails the same way. What tsc cannot pin is accuracy — a\n * reworded doc comment can drift from its sentence — which is why each row\n * stays ONE sentence in the union's own voice (what happened, what it means,\n * whether re-running can work) rather than growing a paragraph of its own.\n */\nimport type { ErrorCode } from \"../envelope/json.js\";\n\nexport const REFUSAL_MEANINGS = {\n \"no-config\":\n \"No loam.json at or above the working directory — the repo is not wired to a fleet; run `loam init --docs <dir>` once per repository rather than letting a command guess where the fleet lives.\",\n \"config-invalid\":\n \"loam.json exists but cannot be loaded — repair the file (or delete it and re-run `loam init`); the code is distinct from `no-config` because running init over a corrupt config would silently rewrite it.\",\n \"unknown-target\":\n \"The name resolves to nothing this command can address — no such service, feature, workflow or subject; the message offers the closest known names.\",\n \"invalid-option\":\n \"The invocation itself is wrong — flags that contradict each other, a value that cannot be right, or a mistyped flag or command; nothing was read or written, and with `--json` anywhere in the arguments even commander's own usage errors arrive as this envelope.\",\n \"already-exists\":\n \"Refusing to create over something that already exists — a feature directory, a workspace file, a name already claimed in the subsystem namespace — because overwriting would destroy work nobody asked to lose.\",\n \"sources-absent\":\n \"`loam vouch` refuses to stamp a document that names no `sources` — with nothing tying the document to code, the staleness signal a vouch exists to power could never fire.\",\n \"sources-path-missing\":\n \"`loam vouch` refuses to stamp: a listed source does not exist in the service repo, or is a glob pattern (not supported) — the digest would cover a different file set than the author meant.\",\n \"not-coherent\":\n \"`loam archive` refuses: the feature has coherence issues, carried in `issues[]` with a per-issue `overridable` — fix the gating ones, or `--approve` the judgment calls; a refusal `--approve` cannot move says so in the message.\",\n \"living-outside-requirements\":\n \"`loam archive` refuses: the LIVING spec holds a requirement outside its `## Requirements` section, and the merge rewrites only that section, so the requirement would land in the file twice — the fix is in the living document, and `--approve` does not override it.\",\n \"archive-exists\":\n \"`loam archive` refuses: `features/archive/` already holds a directory for this feature — a destination collision, not a merge problem; move the old archive entry aside first.\",\n \"merge-failed\":\n \"The merge — or a journaled writer's one-file commit — could not be computed or was rolled back; the living docs are unchanged, and re-running can work.\",\n \"rollback-incomplete\":\n \"A failed write could not be fully undone: some files were not put back, and the message lists them — the one refusal that needs a human before anything else runs against the repo.\",\n \"feature-active\":\n \"`loam unarchive` refuses: a `features/` directory for this feature already exists outside the archive, so restoring would write over work in flight — remove or rename it, then re-run.\",\n \"snapshot-missing\":\n \"`loam unarchive` refuses: the archive carries no snapshot of what it overwrote (it predates snapshots, or was written by a different loam) — restore the living docs from version control instead.\",\n \"snapshot-stale\":\n \"`loam unarchive` refuses: the living docs changed after the archive, so restoring is no longer an undo — re-run with `--force` to restore anyway, deliberately discarding those later changes.\",\n \"snapshot-corrupt\":\n \"`loam unarchive` refuses: a snapshot pre-image no longer matches the digest the archive recorded for it, so restoring would write text nobody wrote — `--force` does not override this one; restore from version control.\",\n \"commit-interrupted\":\n \"A journaled writer (archive, unarchive, rebase, vouch, new, gherkin, a recording verify) was killed mid-commit and this run cannot repair the leftovers on its own — the docs may be half-written; run the command the message names, or `loam doctor` for the file-by-file account.\",\n \"restore-failed\":\n \"`loam unarchive` failed and everything was rolled back — nothing was restored, the living docs are unchanged, and re-running can work.\",\n \"answers-unreadable\":\n \"A recording `loam verify` could not read a user-named file — the answers JSON, or a cucumber/contract report — as what it claims to be; fail-closed: nothing was recorded, so fix the file and re-run.\",\n \"answers-mismatch\":\n \"The answer set does not answer the current checklist — an id not on it, a claim with no answer, or an entry for a claim a passed report already owns; re-derive the checklist and answer exactly what it asks.\",\n \"answers-unevidenced\":\n \"A `confirmed` answer carries no evidence — evidence is `file:line`, and a claim you cannot show is `unconfirmed` with a note saying why, which is a successful record, not a failure.\",\n \"service-mismatch\":\n \"A command that must run inside one service's own repository was told `--service` for a service this repository's loam.json does not declare: a recording `loam verify`, whose attestation must be bound to the code it is about; `loam gherkin`, whose .feature files land in that service's checkout; or `loam vouch`, whose `sources` resolve there. Run it from that service's own repo.\",\n \"unknown-service\":\n \"Federated verify recording: the selected service is not one this feature's checklist (or the docs repo) knows, so there is nothing its attestation could bind to — fix the id.\",\n \"repository-unavailable\":\n \"The repository this operation must be bound to (or read from) cannot be answered for — most commonly a writing `loam verify`, a `loam gherkin` or a `loam vouch` run where loam.json declares no `service` at all (the docs repo, usually), or a docs repo that cannot be enumerated — so the command refuses rather than guessing which service it is about.\",\n \"record-federated\":\n \"`loam verify --record` without `--service` over a federated (schema 2) record — the all-at-once form would erase other repositories' attestations, so it is refused naming the attestors; record per service instead.\",\n \"record-unreadable\":\n \"A verification.yaml exists but cannot be read as a record — it is never overwritten and never reported as absent; repair it by hand, or re-record from the service's repo.\",\n \"record-raced\":\n \"`loam verify --record` found the record changed between its locked read and the swap — an editor or a lock-ignoring writer landed first; nothing was written, and re-running merges over the record as it now stands.\",\n \"gherkin-conflict\":\n \"`loam gherkin <FEAT>` would overwrite a `.feature` file owned by another feature still in flight — the whole emission refuses and names the owner; archive or coordinate with that feature first.\",\n \"vouch-raced\":\n \"`loam vouch` found the spec changed between reading and stamping — another vouch or an edit landed first; nothing was written, so re-read and re-run.\",\n \"vouch-unattended\":\n \"`loam vouch` with nobody on the other end of stdin and no `--yes` — the stamp is a person's claim to have read the document, and nobody was asked.\",\n \"vouch-unattributable\":\n \"`loam vouch` could not learn who is vouching — git names no `user.email` here — and a stamp with nobody behind it records only that the word was written; set your git identity and re-run.\",\n \"vouch-declined\":\n \"`loam vouch` asked and the person said no — nothing was stamped; a successful refusal, not a failure.\",\n \"docs-missing\":\n \"`docsDir` in loam.json points at nothing — the docs repo was never cloned, or the path is wrong; a read command refuses rather than reporting an empty fleet, and `loam doctor` names the wiring to fix.\",\n \"services-missing\":\n \"`docsDir` is a directory but holds no `services/` — it is some other directory, most often the service repo itself after a typo; fix the pointer rather than scaffolding a second source of truth beside the real one.\",\n \"docs-busy\":\n \"Another loam writer holds the docs repo's advisory lock — nothing was read or written, and re-running once it finishes works; a lock nothing will ever release shows up in `loam doctor` as a blocker.\",\n \"subsystem-not-empty\":\n \"`loam subsystem` rm refuses a subsystem that still holds members — services or child subsystems, named in the message — because a destructive command never picks targets the caller did not name; move them out first, then re-run.\",\n \"move-uncommitted\":\n \"A subsystem move/rename refuses because git reports uncommitted or untracked paths under a directory being moved — the rename would sweep them into a move nobody reviewed; commit, `git stash -u`, or remove them, then re-run.\",\n \"move-failed\":\n \"A subsystem move/rename failed and was rolled back cleanly — every rename undone, the generated views file restored, the docs unchanged; re-running can work.\",\n \"no-members\":\n \"`loam open` found no service checkout bound to this docs repo under any scanned root — the workspace would hold only the docs repo; clone a bound checkout beside it, or point `--root` at where the checkouts live.\",\n \"binding-duplicate\":\n \"Two discovered repositories' committed loam.json files declare the same `service` for this docs repo — loam will not guess which checkout speaks for the service; narrow the scan with `--root`, or fix the stray binding.\",\n \"owners-unreadable\":\n \"`loam list --owners` could not use the named CODEOWNERS file — the path cannot be read, or a line does not parse as `pattern owner…` (the message names the line); fail-closed, because a half-read ownership file must never file a service under the wrong team.\",\n \"seed-file-invalid\":\n \"`loam seed`'s fleet file is missing, unreadable, not YAML, the wrong shape, carries an illegal id — or no longer names every existing `services/<id>/` (that refusal lists the exact ids as `missingServices`); the message names the file and line, and editing the file and re-running succeeds.\",\n \"seed-duplicate-service\":\n \"fleet.yaml declares one name twice — as two services, or as both a service and an external/subsystem; service ids, subsystem names and externals share one flat namespace, both declaration lines are named, and renaming one fixes it.\",\n \"seed-unknown-subsystem\":\n \"A service's `subsystem:` in fleet.yaml names nothing `subsystems:` declares — the message carries a did-you-mean over the names the file really declares; add the subsystem or fix the spelling, then re-run.\",\n \"seed-landscape-edited\":\n \"architecture/landscape.likec4 carries hand edits (the line-1 stamp's digest no longer matches) or was authored some other way — `loam seed` never overwrites human work and wrote nothing; fold the edits into fleet.yaml and delete the file, or keep the hand-authored map and stop using seed.\",\n internal:\n \"An unexpected throw — the one refusal with no stable meaning; it exists so a `--json` consumer still receives an envelope instead of a stack trace, and a repeatable `internal` is a loam defect worth reporting.\",\n} as const satisfies Record<ErrorCode, string>;\n"]}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The concept terms `loam explain` answers — the vocabulary wall an agent hits
|
|
3
|
+
* in its first minute, before any docs repo exists to read.
|
|
4
|
+
*
|
|
5
|
+
* Each paragraph is authored HERE, but it is not free to drift: every entry
|
|
6
|
+
* carries pin phrases lifted VERBATIM from the section that teaches the
|
|
7
|
+
* concept, and test/explain.test.ts asserts each phrase appears both in the
|
|
8
|
+
* paragraph and in its named source constant. Rewording that prose therefore
|
|
9
|
+
* breaks the pairing loudly — the same discipline the compiler applies to
|
|
10
|
+
* refusals.ts, done with a test because prose has no type. A pin phrase is
|
|
11
|
+
* kept to one source LINE, because the sections wrap at ~80 columns and a
|
|
12
|
+
* phrase spanning a wrap would never match.
|
|
13
|
+
*
|
|
14
|
+
* A source is no longer always an agents-md section. When the reference pages
|
|
15
|
+
* were split out of the scaffolded AGENTS.md, the frontmatter teaching went
|
|
16
|
+
* with them, and `vouch`'s pin went with the text rather than being reworded
|
|
17
|
+
* to something still in the file — the pairing is with whatever loam SHIPS as
|
|
18
|
+
* the teaching, not with one delivery of it.
|
|
19
|
+
*/
|
|
20
|
+
import { ARTIFACTS } from "../agent/agents-md/artifacts.js";
|
|
21
|
+
import { CYCLE } from "../agent/agents-md/cycle.js";
|
|
22
|
+
import { REFUSALS } from "../agent/agents-md/refusals.js";
|
|
23
|
+
import { SPINE } from "../agent/agents-md/spine.js";
|
|
24
|
+
import { LOAM_AUTHORING } from "../agent/workflows/reference/authoring.js";
|
|
25
|
+
import { LOAM_DONE_CHECK } from "../agent/workflows/reference/done-check.js";
|
|
26
|
+
import { LOAM_SPINE } from "../agent/workflows/reference/spine.js";
|
|
27
|
+
/**
|
|
28
|
+
* The section constants the pins point at, for the drift test and for anyone
|
|
29
|
+
* auditing a pairing.
|
|
30
|
+
*
|
|
31
|
+
* `REFUSALS` is still listed and still pinned against, and it is no longer a
|
|
32
|
+
* section of AGENTS.md — it is the refusal half of the `loam-codes` page. The
|
|
33
|
+
* constant is what a pin names, and the constant did not move; only its
|
|
34
|
+
* delivery did.
|
|
35
|
+
*/
|
|
36
|
+
export const PIN_SOURCE_TEXT = {
|
|
37
|
+
SPINE,
|
|
38
|
+
SPINE_PAGE: LOAM_SPINE.body,
|
|
39
|
+
REFUSALS,
|
|
40
|
+
CYCLE,
|
|
41
|
+
ARTIFACTS,
|
|
42
|
+
AUTHORING: LOAM_AUTHORING.body,
|
|
43
|
+
DONE_CHECK: LOAM_DONE_CHECK.body,
|
|
44
|
+
};
|
|
45
|
+
export const TERMS = [
|
|
46
|
+
{
|
|
47
|
+
term: "vouch",
|
|
48
|
+
aliases: ["vouched", "vouching"],
|
|
49
|
+
paragraph: "A vouch is the human promotion of a living document from draft to verified. " +
|
|
50
|
+
"Promoting draft to verified is their call, not yours: a person runs `loam vouch --service <id>` in the service's own repo, " +
|
|
51
|
+
"and the stamp records the date, who vouched (from git), and digests of both the source files and the document body — " +
|
|
52
|
+
"which is what lets a later `loam validate` report `sources.stale` and `content.stale` when the code or the words move. " +
|
|
53
|
+
"**`loam vouch` is not yours to run**, and it is built so that it cannot be. It refuses unattended runs (`vouch-unattended`), " +
|
|
54
|
+
"records a no as `vouch-declined`, and refuses to stamp when git can name nobody (`vouch-unattributable`); " +
|
|
55
|
+
"when your work leaves a document ready, hand back and say a vouch is owed.",
|
|
56
|
+
pins: [
|
|
57
|
+
// Both phrases left AGENTS.md with the frontmatter teaching and are now
|
|
58
|
+
// in `loam instructions loam-authoring`. Repaired by following the text,
|
|
59
|
+
// not by rewording the paragraph to something still in the file: the
|
|
60
|
+
// pairing exists so this paragraph and the teaching cannot drift, and
|
|
61
|
+
// re-pinning to a different sentence would have quietly ended it.
|
|
62
|
+
{ source: "AUTHORING", phrase: "Promoting draft to verified is their call, not yours" },
|
|
63
|
+
{ source: "AUTHORING", phrase: "**`loam vouch` is not yours to run**, and it is built so that it cannot be." },
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
term: "attested",
|
|
68
|
+
aliases: ["attest", "attestation"],
|
|
69
|
+
paragraph: "Attested is the verdict one rung below verified on the done-check's ladder. " +
|
|
70
|
+
"Verified means somebody says the code was built and showed evidence — and for scenario claims that evidence is a " +
|
|
71
|
+
"test runner's report, answered mechanically. Without a runnable suite your word can still answer them " +
|
|
72
|
+
"(`loam verify <FEAT> --record` alone), but the record says who answered, and a feature whose " +
|
|
73
|
+
"scenario claims rest on an agent's word is **attested**, not verified. Nothing gates on the difference; " +
|
|
74
|
+
"pass `--results <report>` the moment a suite runs and the verdict moves on its own.",
|
|
75
|
+
pins: [
|
|
76
|
+
{ source: "REFUSALS", phrase: "somebody says the code was built and showed evidence" },
|
|
77
|
+
// Left the cycle with the done-check teaching; repaired by following the
|
|
78
|
+
// text, as `vouch`'s two were.
|
|
79
|
+
{ source: "DONE_CHECK", phrase: "scenario claims rest on an agent's word is **attested**, not verified" },
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
term: "spine",
|
|
84
|
+
aliases: [],
|
|
85
|
+
paragraph: "The spine is a family of exact joins, not one magic id. Its ids are the feature tag (`#FEAT-101`), the service " +
|
|
86
|
+
"binding (`metadata { service }`), the synchronous operationId, the asynchronous message name, `Covers:` " +
|
|
87
|
+
"architecture coverage, `Requires:` permissions and `Capability:` realization. Three artifacts describe one synchronous call — " +
|
|
88
|
+
"a C4 edge, a requirement's `Operations:` line, the provider's openapi.yaml — joined by the operationId, and that " +
|
|
89
|
+
"join is what `loam validate` checks. Spell it identically in all three places. The `spine.*` findings are exactly " +
|
|
90
|
+
"these joins failing to resolve, and a mismatch is a broken contract between services, not a style problem.",
|
|
91
|
+
pins: [
|
|
92
|
+
{ source: "SPINE_PAGE", phrase: "The spine is a family of exact joins, not one magic id" },
|
|
93
|
+
{ source: "SPINE_PAGE", phrase: "Spell it identically in all three places." },
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
term: "delta",
|
|
98
|
+
aliases: ["deltas"],
|
|
99
|
+
paragraph: "A delta is a feature's change, stated against the living docs rather than in them. " +
|
|
100
|
+
"The living spec.md is the complete current state; a feature's copy is a diff against it, reviewed as a diff, with " +
|
|
101
|
+
"requirements grouped under `## ADDED`, `## MODIFIED` or `## REMOVED Requirements`; delta.likec4 carries the " +
|
|
102
|
+
"architecture change with every changed element tagged `#<FEAT>` (untagged elements are context, not changes); " +
|
|
103
|
+
"and the contract deltas are complete documents whose baseline pins mark what is merely quoted. " +
|
|
104
|
+
"`loam archive` folds the delta into the living state. The `delta.*` findings grade that algebra — whether the " +
|
|
105
|
+
"diff still applies to the living document it claims to change.",
|
|
106
|
+
pins: [
|
|
107
|
+
{ source: "ARTIFACTS", phrase: "is a diff against it, reviewed as a diff" },
|
|
108
|
+
{ source: "ARTIFACTS", phrase: "`loam archive` folds the delta into the living state." },
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
term: "axis",
|
|
113
|
+
aliases: ["axes"],
|
|
114
|
+
paragraph: "An axis is one language a service is described in, and loam's checks are joins BETWEEN axes rather than opinions within one. " +
|
|
115
|
+
"The three: architecture — a C4 edge names the operation it calls; behaviour — a requirement declares the " +
|
|
116
|
+
"operations it governs; contract — the provider's `openapi.yaml` defines it (asyncapi.yaml is the same axis for " +
|
|
117
|
+
"events, and arch.spec.md is the behaviour axis for architectural obligations). A finding code's prefix names the " +
|
|
118
|
+
"axes that disagree — `spec-api.op-undefined` is the behaviour axis joined against the contract axis — and the fix " +
|
|
119
|
+
"is whichever side is wrong about the world.",
|
|
120
|
+
pins: [
|
|
121
|
+
{ source: "SPINE_PAGE", phrase: "architecture — a C4 edge names the operation it calls" },
|
|
122
|
+
{ source: "SPINE_PAGE", phrase: "behaviour — a requirement declares the operations it governs" },
|
|
123
|
+
{ source: "SPINE_PAGE", phrase: "contract — the provider's `openapi.yaml` defines it" },
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
term: "baseline",
|
|
128
|
+
aliases: ["based-on", "baselines"],
|
|
129
|
+
paragraph: "A baseline is the pin tying a delta to the living version it was written against. It rides as `Based-On:` under a " +
|
|
130
|
+
"MODIFIED/REMOVED requirement and `x-loam-based-on` on a contract surface — written by `loam rebase <FEAT>`, never by " +
|
|
131
|
+
"hand. It exists because a MODIFIED requirement carries its FULL new text, not a diff, so the merge REPLACES the " +
|
|
132
|
+
"living text wholesale; the pin is what lets archive tell an edit from a quote and refuse a stale one " +
|
|
133
|
+
"(`delta.baseline-stale`, `openapi.baseline-stale`) instead of silently reverting work that landed in between. " +
|
|
134
|
+
`**Restamping is not resolving.** A pin claims "I read this version". ` +
|
|
135
|
+
"Re-read the living text and fold in what you still mean before you re-pin.",
|
|
136
|
+
pins: [
|
|
137
|
+
// Without the sentence-initial "A": the paragraph embeds the clause
|
|
138
|
+
// mid-sentence, and a pin is a verbatim substring of BOTH sides.
|
|
139
|
+
{ source: "SPINE_PAGE", phrase: "MODIFIED requirement carries its FULL new text, not a diff" },
|
|
140
|
+
{ source: "SPINE_PAGE", phrase: `**Restamping is not resolving.** A pin claims "I read this version".` },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
//# sourceMappingURL=terms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terms.js","sourceRoot":"","sources":["../../../src/core/explain/terms.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The concept terms `loam explain` answers — the vocabulary wall an agent hits\n * in its first minute, before any docs repo exists to read.\n *\n * Each paragraph is authored HERE, but it is not free to drift: every entry\n * carries pin phrases lifted VERBATIM from the section that teaches the\n * concept, and test/explain.test.ts asserts each phrase appears both in the\n * paragraph and in its named source constant. Rewording that prose therefore\n * breaks the pairing loudly — the same discipline the compiler applies to\n * refusals.ts, done with a test because prose has no type. A pin phrase is\n * kept to one source LINE, because the sections wrap at ~80 columns and a\n * phrase spanning a wrap would never match.\n *\n * A source is no longer always an agents-md section. When the reference pages\n * were split out of the scaffolded AGENTS.md, the frontmatter teaching went\n * with them, and `vouch`'s pin went with the text rather than being reworded\n * to something still in the file — the pairing is with whatever loam SHIPS as\n * the teaching, not with one delivery of it.\n */\nimport { ARTIFACTS } from \"../agent/agents-md/artifacts.js\";\nimport { CYCLE } from \"../agent/agents-md/cycle.js\";\nimport { REFUSALS } from \"../agent/agents-md/refusals.js\";\nimport { SPINE } from \"../agent/agents-md/spine.js\";\nimport { LOAM_AUTHORING } from \"../agent/workflows/reference/authoring.js\";\nimport { LOAM_DONE_CHECK } from \"../agent/workflows/reference/done-check.js\";\nimport { LOAM_SPINE } from \"../agent/workflows/reference/spine.js\";\n\nexport type PinSource =\n | \"SPINE\"\n | \"SPINE_PAGE\"\n | \"REFUSALS\"\n | \"CYCLE\"\n | \"ARTIFACTS\"\n | \"AUTHORING\"\n | \"DONE_CHECK\";\n\n/**\n * The section constants the pins point at, for the drift test and for anyone\n * auditing a pairing.\n *\n * `REFUSALS` is still listed and still pinned against, and it is no longer a\n * section of AGENTS.md — it is the refusal half of the `loam-codes` page. The\n * constant is what a pin names, and the constant did not move; only its\n * delivery did.\n */\nexport const PIN_SOURCE_TEXT: Record<PinSource, string> = {\n SPINE,\n SPINE_PAGE: LOAM_SPINE.body,\n REFUSALS,\n CYCLE,\n ARTIFACTS,\n AUTHORING: LOAM_AUTHORING.body,\n DONE_CHECK: LOAM_DONE_CHECK.body,\n};\n\nexport interface TermPin {\n readonly source: PinSource;\n /** A verbatim single-line substring of both the paragraph and the source section. */\n readonly phrase: string;\n}\n\nexport interface TermEntry {\n readonly term: string;\n readonly aliases: readonly string[];\n /** The explanation. Its FIRST sentence doubles as the no-argument listing's summary. */\n readonly paragraph: string;\n readonly pins: readonly TermPin[];\n}\n\nexport const TERMS: readonly TermEntry[] = [\n {\n term: \"vouch\",\n aliases: [\"vouched\", \"vouching\"],\n paragraph:\n \"A vouch is the human promotion of a living document from draft to verified. \" +\n \"Promoting draft to verified is their call, not yours: a person runs `loam vouch --service <id>` in the service's own repo, \" +\n \"and the stamp records the date, who vouched (from git), and digests of both the source files and the document body — \" +\n \"which is what lets a later `loam validate` report `sources.stale` and `content.stale` when the code or the words move. \" +\n \"**`loam vouch` is not yours to run**, and it is built so that it cannot be. It refuses unattended runs (`vouch-unattended`), \" +\n \"records a no as `vouch-declined`, and refuses to stamp when git can name nobody (`vouch-unattributable`); \" +\n \"when your work leaves a document ready, hand back and say a vouch is owed.\",\n pins: [\n // Both phrases left AGENTS.md with the frontmatter teaching and are now\n // in `loam instructions loam-authoring`. Repaired by following the text,\n // not by rewording the paragraph to something still in the file: the\n // pairing exists so this paragraph and the teaching cannot drift, and\n // re-pinning to a different sentence would have quietly ended it.\n { source: \"AUTHORING\", phrase: \"Promoting draft to verified is their call, not yours\" },\n { source: \"AUTHORING\", phrase: \"**`loam vouch` is not yours to run**, and it is built so that it cannot be.\" },\n ],\n },\n {\n term: \"attested\",\n aliases: [\"attest\", \"attestation\"],\n paragraph:\n \"Attested is the verdict one rung below verified on the done-check's ladder. \" +\n \"Verified means somebody says the code was built and showed evidence — and for scenario claims that evidence is a \" +\n \"test runner's report, answered mechanically. Without a runnable suite your word can still answer them \" +\n \"(`loam verify <FEAT> --record` alone), but the record says who answered, and a feature whose \" +\n \"scenario claims rest on an agent's word is **attested**, not verified. Nothing gates on the difference; \" +\n \"pass `--results <report>` the moment a suite runs and the verdict moves on its own.\",\n pins: [\n { source: \"REFUSALS\", phrase: \"somebody says the code was built and showed evidence\" },\n // Left the cycle with the done-check teaching; repaired by following the\n // text, as `vouch`'s two were.\n { source: \"DONE_CHECK\", phrase: \"scenario claims rest on an agent's word is **attested**, not verified\" },\n ],\n },\n {\n term: \"spine\",\n aliases: [],\n paragraph:\n \"The spine is a family of exact joins, not one magic id. Its ids are the feature tag (`#FEAT-101`), the service \" +\n \"binding (`metadata { service }`), the synchronous operationId, the asynchronous message name, `Covers:` \" +\n \"architecture coverage, `Requires:` permissions and `Capability:` realization. Three artifacts describe one synchronous call — \" +\n \"a C4 edge, a requirement's `Operations:` line, the provider's openapi.yaml — joined by the operationId, and that \" +\n \"join is what `loam validate` checks. Spell it identically in all three places. The `spine.*` findings are exactly \" +\n \"these joins failing to resolve, and a mismatch is a broken contract between services, not a style problem.\",\n pins: [\n { source: \"SPINE_PAGE\", phrase: \"The spine is a family of exact joins, not one magic id\" },\n { source: \"SPINE_PAGE\", phrase: \"Spell it identically in all three places.\" },\n ],\n },\n {\n term: \"delta\",\n aliases: [\"deltas\"],\n paragraph:\n \"A delta is a feature's change, stated against the living docs rather than in them. \" +\n \"The living spec.md is the complete current state; a feature's copy is a diff against it, reviewed as a diff, with \" +\n \"requirements grouped under `## ADDED`, `## MODIFIED` or `## REMOVED Requirements`; delta.likec4 carries the \" +\n \"architecture change with every changed element tagged `#<FEAT>` (untagged elements are context, not changes); \" +\n \"and the contract deltas are complete documents whose baseline pins mark what is merely quoted. \" +\n \"`loam archive` folds the delta into the living state. The `delta.*` findings grade that algebra — whether the \" +\n \"diff still applies to the living document it claims to change.\",\n pins: [\n { source: \"ARTIFACTS\", phrase: \"is a diff against it, reviewed as a diff\" },\n { source: \"ARTIFACTS\", phrase: \"`loam archive` folds the delta into the living state.\" },\n ],\n },\n {\n term: \"axis\",\n aliases: [\"axes\"],\n paragraph:\n \"An axis is one language a service is described in, and loam's checks are joins BETWEEN axes rather than opinions within one. \" +\n \"The three: architecture — a C4 edge names the operation it calls; behaviour — a requirement declares the \" +\n \"operations it governs; contract — the provider's `openapi.yaml` defines it (asyncapi.yaml is the same axis for \" +\n \"events, and arch.spec.md is the behaviour axis for architectural obligations). A finding code's prefix names the \" +\n \"axes that disagree — `spec-api.op-undefined` is the behaviour axis joined against the contract axis — and the fix \" +\n \"is whichever side is wrong about the world.\",\n pins: [\n { source: \"SPINE_PAGE\", phrase: \"architecture — a C4 edge names the operation it calls\" },\n { source: \"SPINE_PAGE\", phrase: \"behaviour — a requirement declares the operations it governs\" },\n { source: \"SPINE_PAGE\", phrase: \"contract — the provider's `openapi.yaml` defines it\" },\n ],\n },\n {\n term: \"baseline\",\n aliases: [\"based-on\", \"baselines\"],\n paragraph:\n \"A baseline is the pin tying a delta to the living version it was written against. It rides as `Based-On:` under a \" +\n \"MODIFIED/REMOVED requirement and `x-loam-based-on` on a contract surface — written by `loam rebase <FEAT>`, never by \" +\n \"hand. It exists because a MODIFIED requirement carries its FULL new text, not a diff, so the merge REPLACES the \" +\n \"living text wholesale; the pin is what lets archive tell an edit from a quote and refuse a stale one \" +\n \"(`delta.baseline-stale`, `openapi.baseline-stale`) instead of silently reverting work that landed in between. \" +\n `**Restamping is not resolving.** A pin claims \"I read this version\". ` +\n \"Re-read the living text and fold in what you still mean before you re-pin.\",\n pins: [\n // Without the sentence-initial \"A\": the paragraph embeds the clause\n // mid-sentence, and a pin is a verbatim substring of BOTH sides.\n { source: \"SPINE_PAGE\", phrase: \"MODIFIED requirement carries its FULL new text, not a diff\" },\n { source: \"SPINE_PAGE\", phrase: `**Restamping is not resolving.** A pin claims \"I read this version\".` },\n ],\n },\n];\n"]}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One service as the explorer sees it: what it is, what it touches, and the
|
|
3
|
+
* `loam new` line that would start work on it.
|
|
4
|
+
*
|
|
5
|
+
* Split from the walk in `./explore.ts` because it answers per SERVICE while
|
|
6
|
+
* that answers per QUESTION — the walk decides which services are in the answer
|
|
7
|
+
* and why, this fills each one in. `DescribeRequest` is a record rather than a
|
|
8
|
+
* parameter list for the ordinary reason: nine facts about one service, and no
|
|
9
|
+
* call site should be able to transpose two of them.
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync } from "node:fs";
|
|
12
|
+
import { FleetContext } from "../fleet-context.js";
|
|
13
|
+
import {} from "../c4/likec4.js";
|
|
14
|
+
import { landscapeEvidence, maturityGaps, serviceMaturity } from "../vocabulary/maturity.js";
|
|
15
|
+
import { compareIds } from "../repo/entries.js";
|
|
16
|
+
import { servicePathsAt } from "../repo/paths.js";
|
|
17
|
+
// Type-only, so it is erased and no runtime edge points back at the walk.
|
|
18
|
+
import {} from "./explore.js";
|
|
19
|
+
export async function describe(req) {
|
|
20
|
+
const { id, reason, entry, relationships, svcOf } = req;
|
|
21
|
+
// The edge partition, the modelled probe and the apiExpected rule are ONE
|
|
22
|
+
// derivation (core/vocabulary/maturity.ts `landscapeEvidence`), shared with
|
|
23
|
+
// the context pack's living slice — the same rule spelled twice is how the
|
|
24
|
+
// same service grades `partial` in one command and `documented` in another.
|
|
25
|
+
const { inbound, outbound, modelled, apiExpected } = landscapeEvidence({
|
|
26
|
+
id,
|
|
27
|
+
parses: req.parses,
|
|
28
|
+
relationships,
|
|
29
|
+
elementIds: req.elements.map((e) => e.id),
|
|
30
|
+
svcOf,
|
|
31
|
+
});
|
|
32
|
+
// A service that does not exist has no rung: `empty` would be a claim about a
|
|
33
|
+
// directory nobody has made, and it reads as "exists, nothing in it" — the
|
|
34
|
+
// one state a caller most needs to tell it apart from.
|
|
35
|
+
if (entry === undefined) {
|
|
36
|
+
return {
|
|
37
|
+
id,
|
|
38
|
+
reason,
|
|
39
|
+
known: false,
|
|
40
|
+
maturity: null,
|
|
41
|
+
missing: [],
|
|
42
|
+
modelled,
|
|
43
|
+
operations: [],
|
|
44
|
+
// Not `unreadable`: there is no contract here to fail to read. A service
|
|
45
|
+
// with no directory owes nothing, and reporting a parse failure over an
|
|
46
|
+
// absent file would send a reader looking for a document to fix.
|
|
47
|
+
openapi: { unreadable: false },
|
|
48
|
+
inbound,
|
|
49
|
+
outbound,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const archSpec = existsSync(servicePathsAt(entry.dir).archSpec);
|
|
53
|
+
const input = { entry, archSpec, apiExpected };
|
|
54
|
+
// `readOpenapi`, not `operationIds`: a contract that exists but does not
|
|
55
|
+
// parse comes back from the id list as an EMPTY set, indistinguishable from a
|
|
56
|
+
// service with no endpoints — and "this service offers nothing" is the worst
|
|
57
|
+
// possible lie to tell somebody deciding whether to call it.
|
|
58
|
+
const api = await req.context.readOpenapi(servicePathsAt(entry.dir).openapi);
|
|
59
|
+
const operations = api.ops.filter((o) => !o.remove).map((o) => o.id);
|
|
60
|
+
return {
|
|
61
|
+
id,
|
|
62
|
+
reason,
|
|
63
|
+
known: true,
|
|
64
|
+
maturity: serviceMaturity(input),
|
|
65
|
+
missing: maturityGaps(input),
|
|
66
|
+
modelled,
|
|
67
|
+
operations,
|
|
68
|
+
openapi: {
|
|
69
|
+
unreadable: api.unreadable,
|
|
70
|
+
...(api.error === undefined ? {} : { error: api.error }),
|
|
71
|
+
},
|
|
72
|
+
inbound,
|
|
73
|
+
outbound,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The service whose living contract defines an operationId, or null.
|
|
78
|
+
*
|
|
79
|
+
* Reads every service's `openapi.yaml`, which is the cost of asking a question
|
|
80
|
+
* nothing indexes. It is YAML, not LikeC4 — the expensive parse in this
|
|
81
|
+
* codebase — and it happens only when somebody passes `--op`.
|
|
82
|
+
*/
|
|
83
|
+
export async function operationOwner(entries, op, context) {
|
|
84
|
+
for (const entry of entries) {
|
|
85
|
+
if (!entry.has.openapi)
|
|
86
|
+
continue;
|
|
87
|
+
const api = await context.readOpenapi(servicePathsAt(entry.dir).openapi);
|
|
88
|
+
if (api.ops.some((o) => o.id === op && !o.remove))
|
|
89
|
+
return entry.id;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The `loam new` line the seeds imply. A seed that names no existing service is
|
|
95
|
+
* `--new-service`, not `--touches`: those are different scaffolds, and getting
|
|
96
|
+
* it wrong is what leaves a feature with a requirement delta for a service that
|
|
97
|
+
* has no directory to archive into.
|
|
98
|
+
*/
|
|
99
|
+
export function newCommand(featureId, seeds, known) {
|
|
100
|
+
const flags = [...seeds]
|
|
101
|
+
.sort(compareIds)
|
|
102
|
+
.map((id) => (known.has(id) ? `--touches ${id}` : `--new-service ${id}`));
|
|
103
|
+
return `loam new ${featureId}${flags.length > 0 ? ` ${flags.join(" ")}` : ""}`;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=describe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"describe.js","sourceRoot":"","sources":["../../../src/core/explore/describe.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One service as the explorer sees it: what it is, what it touches, and the\n * `loam new` line that would start work on it.\n *\n * Split from the walk in `./explore.ts` because it answers per SERVICE while\n * that answers per QUESTION — the walk decides which services are in the answer\n * and why, this fills each one in. `DescribeRequest` is a record rather than a\n * parameter list for the ordinary reason: nine facts about one service, and no\n * call site should be able to transpose two of them.\n */\nimport { existsSync } from \"node:fs\";\nimport { FleetContext } from \"../fleet-context.js\";\nimport { type Elem, type Rel } from \"../c4/likec4.js\";\nimport { landscapeEvidence, maturityGaps, serviceMaturity } from \"../vocabulary/maturity.js\";\nimport { compareIds, type ServiceEntry } from \"../repo/entries.js\";\nimport { servicePathsAt } from \"../repo/paths.js\";\n// Type-only, so it is erased and no runtime edge points back at the walk.\nimport { type ExploreReason, type ExploreService } from \"./explore.js\";\n\ninterface DescribeRequest {\n id: string;\n reason: ExploreReason;\n entry: ServiceEntry | undefined;\n relationships: Rel[];\n svcOf: (id: string) => string;\n elements: Elem[];\n /** Whether the landscape parsed — what makes its silence evidence rather than absence. */\n parses: boolean;\n context: FleetContext;\n}\n\nexport async function describe(req: DescribeRequest): Promise<ExploreService> {\n const { id, reason, entry, relationships, svcOf } = req;\n\n // The edge partition, the modelled probe and the apiExpected rule are ONE\n // derivation (core/vocabulary/maturity.ts `landscapeEvidence`), shared with\n // the context pack's living slice — the same rule spelled twice is how the\n // same service grades `partial` in one command and `documented` in another.\n const { inbound, outbound, modelled, apiExpected } = landscapeEvidence({\n id,\n parses: req.parses,\n relationships,\n elementIds: req.elements.map((e) => e.id),\n svcOf,\n });\n\n // A service that does not exist has no rung: `empty` would be a claim about a\n // directory nobody has made, and it reads as \"exists, nothing in it\" — the\n // one state a caller most needs to tell it apart from.\n if (entry === undefined) {\n return {\n id,\n reason,\n known: false,\n maturity: null,\n missing: [],\n modelled,\n operations: [],\n // Not `unreadable`: there is no contract here to fail to read. A service\n // with no directory owes nothing, and reporting a parse failure over an\n // absent file would send a reader looking for a document to fix.\n openapi: { unreadable: false },\n inbound,\n outbound,\n };\n }\n\n const archSpec = existsSync(servicePathsAt(entry.dir).archSpec);\n const input = { entry, archSpec, apiExpected };\n\n // `readOpenapi`, not `operationIds`: a contract that exists but does not\n // parse comes back from the id list as an EMPTY set, indistinguishable from a\n // service with no endpoints — and \"this service offers nothing\" is the worst\n // possible lie to tell somebody deciding whether to call it.\n const api = await req.context.readOpenapi(servicePathsAt(entry.dir).openapi);\n const operations = api.ops.filter((o) => !o.remove).map((o) => o.id);\n\n return {\n id,\n reason,\n known: true,\n maturity: serviceMaturity(input),\n missing: maturityGaps(input),\n modelled,\n operations,\n openapi: {\n unreadable: api.unreadable,\n ...(api.error === undefined ? {} : { error: api.error }),\n },\n inbound,\n outbound,\n };\n}\n\n/**\n * The service whose living contract defines an operationId, or null.\n *\n * Reads every service's `openapi.yaml`, which is the cost of asking a question\n * nothing indexes. It is YAML, not LikeC4 — the expensive parse in this\n * codebase — and it happens only when somebody passes `--op`.\n */\nexport async function operationOwner(\n entries: ServiceEntry[],\n op: string,\n context: FleetContext,\n): Promise<ServiceEntry[\"id\"] | null> {\n for (const entry of entries) {\n if (!entry.has.openapi) continue;\n const api = await context.readOpenapi(servicePathsAt(entry.dir).openapi);\n if (api.ops.some((o) => o.id === op && !o.remove)) return entry.id;\n }\n return null;\n}\n\n/**\n * The `loam new` line the seeds imply. A seed that names no existing service is\n * `--new-service`, not `--touches`: those are different scaffolds, and getting\n * it wrong is what leaves a feature with a requirement delta for a service that\n * has no directory to archive into.\n */\nexport function newCommand(featureId: string, seeds: string[], known: ReadonlySet<string>): string {\n const flags = [...seeds]\n .sort(compareIds)\n .map((id) => (known.has(id) ? `--touches ${id}` : `--new-service ${id}`));\n return `loam new ${featureId}${flags.length > 0 ? ` ${flags.join(\" \")}` : \"\"}`;\n}\n"]}
|