@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 @@
|
|
|
1
|
+
{"version":3,"file":"findings.js","sourceRoot":"","sources":["../../../src/core/capabilities/findings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAoB,MAAM,qBAAqB,CAAC;AAa1E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAA2B;IAC/D,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC/D,OAAO,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;AAChC,CAAC;AASD,iHAAiH;AACjH,SAAS,cAAc,CAAC,IAAmB,EAAE,KAA2B;IACtE,MAAM,KAAK,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAmB,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS;YAAE,SAAS;QACnC,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,YAAY,EAAE,CAAC;YACnC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,SAAS;YACpC,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,cAAc,CAAC,KAAa,EAAE,CAAe;IACpD,OAAO,CACL,GAAG,KAAK,kBAAkB,CAAC,CAAC,WAAW,oBAAoB,CAAC,CAAC,KAAK,+EAA+E,CAAC,CAAC,KAAK,UAAU;QAClK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;YACjB,CAAC,CAAC,mBAAmB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YAC1C,CAAC,CAAC,kKAAkK,CAAC,CACxK,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,IAAmB,EACnB,MAAwB,EACxB,KAA2B;IAE3B,OAAO,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,QAAQ,EAAE,OAAgB;QAC1B,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,OAAO,EAAE,cAAc,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;KACzC,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,IAAmB,EACnB,MAAwB,EACxB,KAA2B;IAE3B,OAAO,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,QAAQ,EAAE,OAAgB;QAC1B,IAAI,EAAE,oBAA6B;QACnC,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,OAAO,EAAE,cAAc,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;KACzC,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CAAC,KAA2B;IAClE,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC7C,OAAO;QACL,QAAQ,EAAE,OAAO;QACjB,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EACL,6EAA6E,KAAK,CAAC,OAAO,IAAI;YAC9F,sJAAsJ;YACtJ,wGAAwG;KAC3G,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAA2B,EAAE,IAAyB;IACvF,MAAM,QAAQ,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC9C,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACjC,OAAO,QAAQ;SACZ,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;SAC7B,IAAI,CAAC,UAAU,CAAC;SAChB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACZ,QAAQ,EAAE,MAAe;QACzB,IAAI,EAAE,uBAAuB;QAC7B,OAAO,EAAE,EAAE;QACX,OAAO,EACL,0BAA0B,EAAE,oBAAoB,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC,0CAA0C;YAC/G,kIAAkI;YAClI,4JAA4J;KAC/J,CAAC,CAAC,CAAC;AACR,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU,CAAC,KAA2B,EAAE,EAAU;IACzD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAC1C,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,gBAAgB,EAAE,UAAU,CAAC;IAC3D,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,mDAAmD,EAAE,UAAU,CAAC;IAC9F,OAAO,gCAAgC,CAAC;AAC1C,CAAC;AAED,wEAAwE;AACxE,0EAA0E;AAC1E,0EAA0E;AAC1E,wEAAwE;AAExE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAoB,EAAE,cAAuB;IAC9E,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACtC,MAAM,IAAI,GAAG,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,cAAc,IAAI,MAAM,EAAE,CAAC;QAC5F,MAAM,IAAI,GACR,cAAc,KAAK,SAAS;YAC1B,CAAC,CAAC,6FAA6F;gBAC7F,SAAS,IAAI,8EAA8E;YAC7F,CAAC,CAAC,4HAA4H;gBAC5H,SAAS,IAAI,0EAA0E,CAAC;QAC9F,OAAO;YACL,QAAQ,EAAE,MAAe;YACzB,IAAI,EAAE,wBAAwB;YAC9B,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,GAAG,IAAI,sIAAsI,IAAI,EAAE;SAC7J,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,aAAa,GAAG;IACpB,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,4CAA4C,EAAE;IACjG,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,qCAAqC,EAAE;IAClF,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,wCAAwC,EAAE;IAC3F,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,wCAAwC,EAAE;CACE,CAAC;AAE9F;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,cAAc,GAAG;IACrB;QACE,KAAK,EAAE,cAAc;QACrB,IAAI,EAAE,aAAa;QACnB,GAAG,EAAE,gHAAgH;KACtH;IACD;QACE,KAAK,EAAE,UAAU;QACjB,IAAI,EAAE,WAAW;QACjB,GAAG,EAAE,6GAA6G;KACnH;CACwF,CAAC;AAoB5F,MAAM,UAAU,qBAAqB,CAAC,IAAmB,EAAE,EAAU,EAAE,KAAK,GAAG,gBAAgB,EAAE,UAAU;IACzG,MAAM,QAAQ,GAAe,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YACvB,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,qCAAqC;gBAC3C,OAAO,EAAE,EAAE;gBACX,OAAO,EACL,GAAG,KAAK,kBAAkB,CAAC,CAAC,IAAI,qCAAqC;oBACrE,kJAAkJ;oBAClJ,8GAA8G;oBAC9G,oDAAoD;aACvD,CAAC,CAAC;QACL,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;YACjC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACzC,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,uCAAuC;gBAC7C,OAAO,EAAE,EAAE;gBACX,OAAO,EACL,GAAG,KAAK,kBAAkB,CAAC,CAAC,IAAI,eAAe,IAAI,CAAC,IAAI,mBAAmB,IAAI,CAAC,KAAK,IAAI;oBACzF,iKAAiK;oBACjK,sFAAsF;oBACtF,oHAAoH;aACvH,CAAC,CAAC;QACL,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;YAClC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACzC,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,mCAAmC;gBACzC,OAAO,EAAE,EAAE;gBACX,OAAO,EACL,GAAG,KAAK,kBAAkB,CAAC,CAAC,IAAI,eAAe,IAAI,CAAC,IAAI,oDAAoD;oBAC5G,GAAG,IAAI,CAAC,GAAG,qGAAqG;oBAChH,wGAAwG;oBACxG,eAAe,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,kBAAkB,KAAK;aACvD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,KAA2B,EAC3B,IAA8C;IAE9C,MAAM,QAAQ,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,YAAY,GAAG,IAAI,GAAG,EAA+B,CAAC;IAC5D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAuC,CAAC;IAC/D,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;IAClE,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClC,wEAAwE;QACxE,sEAAsE;QACtE,wEAAwE;QACxE,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;QAC5E,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAG,CAAC,CAAC,CAAC,CAAC;QAC1D,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAG,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;AAC7C,CAAC","sourcesContent":["/**\n * The capability grades, in every direction the axis is graded: a `Capability:`\n * entry the vocabulary does not declare, a vocabulary nobody can read, a\n * declared capability no living requirement realizes, a directory that declares\n * nothing, and the two rules an authored capability document itself must keep.\n * Values only — core never prints — and the join rule shaped twice (Finding for\n * validate, Issue for coherence), both spelled with literal codes so\n * test/codes-drift.test.ts can collect them.\n *\n * The opt-in rule lives here, and it deliberately DIVERGES from `Requires:`.\n * There the LINE is the opt-in: a `Requires:` entry against a missing\n * permissions.yaml errors, because an authorization join that resolves against\n * nothing is the failure that axis exists to prevent. Here the FLEET'S OWN\n * FILES are the opt-in — `architecture/capabilities.yaml`, the `capabilities/`\n * tree, or both — so a fleet holding neither stays finding-free however many\n * `Capability:` lines its requirements already carry. (The roadmap's exit\n * criterion was written when the YAML was the only side and says 'a fleet with\n * no architecture/capabilities.yaml produces no capability findings at all';\n * the authored tree is the second way to opt in, and `CapabilityVocabulary.present`\n * is where the two are combined.) An INVALID vocabulary is also silence for the\n * per-entry grades — `capability.invalid` is the honest finding there, and\n * grading a hundred entries against a file nobody can read is the cascade that\n * suppression exists to stop.\n */\nimport { closeIds } from \"../c4/arch.js\";\nimport { compareIds } from \"../repo/entries.js\";\nimport { requirementDigest, type Requirement } from \"../document/spec.js\";\nimport type { Finding } from \"../vocabulary/report.js\";\nimport type { CapabilityDocCode, Issue } from \"../vocabulary/issue.js\";\nimport type { CapabilityVocabulary } from \"./capabilities.js\";\nimport type { CapabilityTree } from \"./tree.js\";\nimport type { CapabilityRequirementIndex } from \"./realizes/join.js\";\n\n/** Which document is being graded, and under whose name the finding is filed. */\nexport interface CapabilityTarget {\n where: string;\n subject: string;\n}\n\n/**\n * THE LADDER, and the only place it is applied: the declared capability ids when\n * the vocabulary can be graded against at all, `null` when it cannot.\n *\n * Exported because the suppression is not this module's private business — the\n * use-case axis asks the same question about the same file\n * (`commands/validate/fleet/landscape.ts` hands the answer to\n * `usecases/capability-tag.ts`), and a hand-rolled\n * `present && invalid === undefined ? [...byId.keys()] : null` at each site is\n * three copies of one rule. The failure that costs is not hypothetical: the next\n * un-gradable state added to `CapabilityVocabulary` — \"present but partially\n * readable\" is the obvious one, since `readCapabilities` already treats a\n * non-mapping declaration body as `{}` — gets fixed wherever the greps land, and\n * any copy left behind answers with the whole key set. A half-read vocabulary\n * then reads as a complete one, and every `#cap-` tag whose capability lived in\n * the unreadable half becomes one `usecase.capability-unresolved` ERROR per\n * view: the cascade this ladder exists to prevent, arriving through the ladder.\n *\n * `null` rather than `[]`, and the two are never interchangeable: an empty list\n * is the real verdict \"this fleet declares no capabilities\" and every entry\n * fails against it, while `null` says there is nothing to grade against and\n * suspends the family. Callers that only need the boolean read `=== null`.\n */\nexport function gradableCapabilityIds(vocab: CapabilityVocabulary): readonly string[] | null {\n if (!vocab.present || vocab.invalid !== undefined) return null;\n return [...vocab.byId.keys()];\n}\n\n/** One unresolved entry: the requirement that wrote it, the entry, the close names. */\ninterface UnknownEntry {\n requirement: string;\n entry: string;\n close: string[];\n}\n\n/** Non-REMOVED requirements' entries the vocabulary does not declare — or nothing, per the opt-in rule above. */\nfunction unknownEntries(reqs: Requirement[], vocab: CapabilityVocabulary): UnknownEntry[] {\n const known = gradableCapabilityIds(vocab);\n if (known === null) return [];\n const out: UnknownEntry[] = [];\n for (const r of reqs) {\n if (r.kind === \"REMOVED\") continue;\n for (const entry of r.capabilities) {\n if (vocab.byId.has(entry)) continue;\n out.push({ requirement: r.name, entry, close: closeIds(entry, known) });\n }\n }\n return out;\n}\n\nfunction unknownMessage(where: string, u: UnknownEntry): string {\n return (\n `${where}: requirement '${u.requirement}' — Capability: '${u.entry}' is declared neither in architecture/capabilities.yaml nor as capabilities/${u.entry}/spec.md` +\n (u.close.length > 0\n ? `. Did you mean: ${u.close.join(\", \")}?`\n : \". Declare it in either — a name alone is a `capabilities: {<id>: {description, owner}}` entry, a name with prose behind it is the document — or fix the spelling\")\n );\n}\n\n/**\n * `capability.unknown` as validate sees it — an ERROR, the same grade as\n * `Requires:` and for its reason: an invented capability reads exactly like a\n * real one in the requirement and in every rollup built over it.\n */\nexport function capabilityUnknownFindings(\n reqs: Requirement[],\n target: CapabilityTarget,\n vocab: CapabilityVocabulary,\n): Finding[] {\n return unknownEntries(reqs, vocab).map((u) => ({\n severity: \"error\" as const,\n code: \"capability.unknown\",\n subject: target.subject,\n message: unknownMessage(target.where, u),\n }));\n}\n\n/**\n * The same rule shaped as coherence Issues, for a feature's delta documents: a\n * delta requirement naming an undeclared capability would merge a join that\n * resolves to nothing, so it gates `loam archive` like other errors and stays\n * `--approve`-overridable. The one new archive gate of this axis.\n */\nexport function capabilityUnknownIssues(\n reqs: Requirement[],\n target: CapabilityTarget,\n vocab: CapabilityVocabulary,\n): Issue[] {\n return unknownEntries(reqs, vocab).map((u) => ({\n severity: \"error\" as const,\n code: \"capability.unknown\" as const,\n subject: target.subject,\n message: unknownMessage(target.where, u),\n }));\n}\n\n/**\n * `capability.invalid` — the whole run's ONE finding about an unreadable\n * vocabulary, spelling the expected shape. Every grade that RESOLVES AGAINST\n * the vocabulary stays suspended behind it (see the module comment); the\n * authored tree's own grades do not, because none of them consults it — a\n * directory holding no document, a requirement without a stable id and a\n * requirement carrying a service-level join are facts about files that a broken\n * YAML does not make untrue, and hiding them until an unrelated file parses is\n * suppression without the cascade that justifies it.\n */\nexport function invalidVocabularyFinding(vocab: CapabilityVocabulary): Finding | null {\n if (vocab.invalid === undefined) return null;\n return {\n severity: \"error\",\n code: \"capability.invalid\",\n message:\n `landscape: architecture/capabilities.yaml does not read as a vocabulary — ${vocab.invalid}. ` +\n \"Every `Capability:` line in the fleet resolves against this file, so the whole capability family is suspended until it parses — fix the YAML first. \" +\n \"The shape is `capabilities: {<id>: {description, owner}}`, ids flat keys with `/` allowed for nesting.\",\n };\n}\n\n/**\n * `capability.unrealized` — ONE warn per declared capability no living\n * non-REMOVED requirement names, subject = the id, sorted with compareIds.\n * Per capability rather than one rollup finding with details[] (the\n * permissions.unenforced shape) because the roadmap's own exit criterion is\n * 'one warning per capability, not one per service' — each is a distinct\n * promise nobody implemented, or a distinct word nobody adopted.\n */\nexport function unrealizedFindings(vocab: CapabilityVocabulary, used: ReadonlySet<string>): Finding[] {\n const declared = gradableCapabilityIds(vocab);\n if (declared === null) return [];\n return declared\n .filter((id) => !used.has(id))\n .sort(compareIds)\n .map((id) => ({\n severity: \"warn\" as const,\n code: \"capability.unrealized\",\n subject: id,\n message:\n `landscape: capability '${id}' is declared in ${declaredIn(vocab, id)} and nothing in the fleet realizes it — ` +\n \"no living requirement names it with `Capability:` or `Realizes:`, and no `#cap-`/`#req-` tagged flow keeps one of its promises. \" +\n \"Either a promise nobody implemented or a word nobody adopted; write the requirement that realizes it, draw the flow that keeps it, or drop the declaration\",\n }));\n}\n\n/**\n * Where an id was declared, spelled for a message. The fix differs by side —\n * a YAML line is deleted, a document is deleted with its directory — and a\n * message naming the wrong file sends its reader to edit something that is not\n * there. `both` names both because either alone would leave the id declared.\n */\nfunction declaredIn(vocab: CapabilityVocabulary, id: string): string {\n const source = vocab.byId.get(id)?.source;\n if (source === \"tree\") return `capabilities/${id}/spec.md`;\n if (source === \"both\") return `architecture/capabilities.yaml and capabilities/${id}/spec.md`;\n return \"architecture/capabilities.yaml\";\n}\n\n/* ------------------------------------------------------------------ */\n/* The authored tree — the documents themselves, graded on their own */\n/* terms rather than on the join any requirement makes to them. */\n/* ------------------------------------------------------------------ */\n\n/**\n * `capability.doc-missing` — a directory in a `capabilities/` tree that holds no\n * `spec.md` and has no capability beneath it.\n *\n * WARN, not error, and the severity is the whole judgement: the state it names\n * is what `mkdir` leaves behind halfway through creating a capability, and a\n * half-finished authoring step must not fail somebody else's `loam validate`\n * on the same tree. What makes it worth a finding at all is that the directory\n * LOOKS declared — a reader browsing `capabilities/` counts it, and a\n * `Capability:` line naming it still reports `capability.unknown`, which is the\n * confusing pair this warning exists to join up.\n *\n * BOTH TREES EARN IT, and `featureDirName` is which one. The same walk reads\n * `features/<dir>/capabilities/`, so the same `mkdir` leaves the same shape\n * there — quieter, not louder: an empty directory is not a delta, so the delta\n * algebra, the merge and `loam show` alike do not see it and the business change\n * ships as nothing at exit 0. One code, one mistake; two messages, because the\n * path and the fix differ. It is the DIRECTORY name and not the feature id\n * (`FEAT-1-split` is where the directory is), and `undocumented` is spelled from\n * the WALK'S root — literally `capabilities/…` on both sides — so a feature's\n * entry is re-rooted here or the message names a path nobody can open.\n */\nexport function docMissingFindings(tree: CapabilityTree, featureDirName?: string): Finding[] {\n return tree.undocumented.map((walked) => {\n const path = featureDirName === undefined ? walked : `features/${featureDirName}/${walked}`;\n const rest =\n featureDirName === undefined\n ? \"so this one declares nothing and a requirement naming it still reports capability.unknown. \" +\n `Write ${path}/spec.md (the narrative, then \\`## Requirements\\`), or remove the directory.`\n : \"so this one changes nothing — the archive merges the capability deltas the walk FINDS, and an empty directory is not one. \" +\n `Write ${path}/spec.md (a \\`## ADDED Requirements\\` section), or remove the directory.`;\n return {\n severity: \"warn\" as const,\n code: \"capability.doc-missing\",\n subject: path,\n message: `${path}/ holds no spec.md and no capability beneath it — a directory under capabilities/ is a capability only when it holds the document, ${rest}`,\n };\n });\n}\n\n/**\n * The service-level joins, and the whole of what \"names no service\" is checked\n * as. Each entry is a `Requirement` field that is non-empty exactly when the\n * requirement wrote the line and named something in it.\n *\n * THE PROSE RULE IS NOT CHECKED AND MUST NOT BE FAKED. \"A capability\n * requirement names no service\" is an authoring rule about wording, and the\n * only mechanical reading of it — scanning the text for a declared service id —\n * is a heuristic that convicts a payments capability for the word \"payments\".\n * loam refuses that class of check everywhere else and refuses it here; PR\n * review holds the prose. What IS exact is the structural half: these four\n * lines resolve against a SERVICE's contract and model, so a requirement\n * carrying one has already left the business altitude whatever its wording\n * says. `Requires:` is deliberately absent — a permission is a domain fact\n * (\"who may see a profile\"), observable outside the fleet, and gating a\n * capability requirement on one is legitimate.\n */\nconst SERVICE_JOINS = [\n { field: \"operations\", line: \"Operations:\", names: \"an operationId in a service's openapi.yaml\" },\n { field: \"covers\", line: \"Covers:\", names: \"a C4 element, edge or health signal\" },\n { field: \"publishes\", line: \"Publishes:\", names: \"an AsyncAPI message a service produces\" },\n { field: \"consumes\", line: \"Consumes:\", names: \"an AsyncAPI message a service consumes\" },\n] as const satisfies ReadonlyArray<{ field: keyof Requirement; line: string; names: string }>;\n\n/**\n * The two joins that point INTO the capability tree, and are therefore inert\n * written inside it.\n *\n * Not \"wrong at this altitude\" like the four above — these are the axis's own\n * lines. They are refused here because nothing reads them: `capabilityRollup`\n * joins SERVICE requirements to capabilities, so a `Capability:` line in a\n * capability document is a requirement claiming the document it is already in,\n * and a `Realizes:` line there points at a promise loam will never see anybody\n * keep. Both parse, both look exactly like the working joins one directory\n * over, and both would sit in a reviewed document meaning nothing.\n *\n * A capability realizing ANOTHER capability's requirement — a nested\n * `payments/refunds` serving a promise made by `payments` — is a real idea and\n * is deliberately not being smuggled in as a line that happens to parse. It\n * would need a design: a second corpus of joins, its own cycle rule, and an\n * answer for what `capability.requirement-unrealized` means when the realizer\n * is itself unrealized. Refusing the line now is what keeps that decision open.\n */\nconst INTERNAL_JOINS = [\n {\n field: \"capabilities\",\n line: \"Capability:\",\n why: \"the rollup joins SERVICE requirements to capabilities, so written here it claims the document it is already in\",\n },\n {\n field: \"realizes\",\n line: \"Realizes:\",\n why: \"a capability requirement is what gets realized, not what realizes — nothing reads this line inside the tree\",\n },\n] as const satisfies ReadonlyArray<{ field: keyof Requirement; line: string; why: string }>;\n\n/**\n * Both grades a capability document earns on its own: every requirement needs\n * a stable id, and none may carry a service-level join.\n *\n * `where` is the document's repo-relative path and `subject` the capability id,\n * so a `--json` consumer can group by capability while a reader gets the file.\n * The two grades ride one walk because they ask about the same requirements and\n * a second pass is a second chance to disagree about which ones are graded.\n *\n * `where` DEFAULTS to the living spelling; `capabilities/delta/doc.ts` overrides\n * it to grade the same requirements inside a feature's own capability delta and\n * file them as archive Issues. A parameter rather than a second function because\n * these messages are what a reader acts on, and two copies drift. The return type\n * states severity and code exactly for that caller: the Finding→Issue conversion\n * needs no cast, and a fourth grade added here cannot silently stop gating.\n */\ntype DocGrade = Finding & { severity: \"error\"; code: CapabilityDocCode };\n\nexport function capabilityDocFindings(reqs: Requirement[], id: string, where = `capabilities/${id}/spec.md`): DocGrade[] {\n const findings: DocGrade[] = [];\n for (const r of reqs) {\n if (r.id === undefined) {\n findings.push({\n severity: \"error\",\n code: \"capability.requirement-unidentified\",\n subject: id,\n message:\n `${where}: requirement '${r.name}' has no \\`Requirement-ID:\\` line. ` +\n \"A capability document outlives every service that realizes it, so its requirements are identified by a stable id rather than by their heading — \" +\n \"without one, rewording the heading is a removal and an addition, and every join made to it breaks silently. \" +\n \"Add `Requirement-ID: <id>` as the first body line.\",\n });\n }\n for (const join of SERVICE_JOINS) {\n if (r[join.field].length === 0) continue;\n findings.push({\n severity: \"error\",\n code: \"capability.requirement-service-scoped\",\n subject: id,\n message:\n `${where}: requirement '${r.name}' carries \\`${join.line}\\`, which names ${join.names}. ` +\n \"A capability requirement must be observable outside the fleet — a promise a customer could check — and these lines resolve against one service's own contract, \" +\n \"so a requirement carrying one is a service requirement filed at the wrong altitude. \" +\n \"Move it into that service's spec.md, where the line resolves; what belongs here is the promise, not the mechanism.\",\n });\n }\n for (const join of INTERNAL_JOINS) {\n if (r[join.field].length === 0) continue;\n findings.push({\n severity: \"error\",\n code: \"capability.requirement-inert-join\",\n subject: id,\n message:\n `${where}: requirement '${r.name}' carries \\`${join.line}\\`, which does nothing in a capability document — ` +\n `${join.why}. It parses and reads exactly like the working join one directory over, which is why it is refused ` +\n \"rather than ignored. Delete the line; the join that matters is written on the SERVICE requirement, as \" +\n `\\`Realizes: ${id}#${r.id ?? \"<Requirement-ID>\"}\\`.`,\n });\n }\n }\n return findings;\n}\n\n/**\n * The requirement ids each capability document declares, with THE LADDER\n * already applied — the one shape `Realizes:` is ever resolved against.\n *\n * Built here rather than at the three call sites for the reason\n * `gradableCapabilityIds` above is exported: the suppression rule and the index\n * it guards belong together. A caller that composed them itself would be a\n * fourth place where \"can this be graded at all\" is decided, and the first one\n * to be forgotten when a fifth un-gradable vocabulary state arrives.\n *\n * A requirement with no `Requirement-ID:` is absent from the index, so a\n * `Realizes:` entry naming its heading does not resolve. That is deliberate and\n * not a gap: an unidentified capability requirement is already an ERROR\n * (`capability.requirement-unidentified`), and letting a heading work as an\n * address would quietly reintroduce the identity-by-heading the tree exists to\n * refuse. REMOVED is skipped for the reason every other reader skips it.\n *\n * The reads are NOT wrapped: a `capabilities/<id>/spec.md` that cannot be\n * decoded takes the run down exactly as a service spec does — the rule\n * `capabilityFleetFindings` already states one directory over.\n */\nexport async function capabilityRequirementIndex(\n vocab: CapabilityVocabulary,\n read: (path: string) => Promise<Requirement[]>,\n): Promise<CapabilityRequirementIndex> {\n const declared = gradableCapabilityIds(vocab);\n const byCapability = new Map<string, ReadonlySet<string>>();\n const digests = new Map<string, ReadonlyMap<string, string>>();\n if (declared === null) return { declared, byCapability, digests };\n for (const doc of vocab.tree.docs) {\n const reqs = await read(doc.spec);\n // ONE pass fills both, which is what keeps them from disagreeing: an id\n // reaches the set and the digest map in the same iteration or reaches\n // neither. The skip rule is stated once, above the loop body it guards.\n const kept = reqs.filter((r) => r.kind !== \"REMOVED\" && r.id !== undefined);\n byCapability.set(doc.id, new Set(kept.map((r) => r.id!)));\n digests.set(doc.id, new Map(kept.map((r) => [r.id!, requirementDigest(r)])));\n }\n return { declared, byCapability, digests };\n}\n"]}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `Realizes:` grades, both directions: an entry that resolves to nothing,
|
|
3
|
+
* and a capability requirement nothing realizes.
|
|
4
|
+
*
|
|
5
|
+
* Values only — core never prints — and the join rule shaped twice (Finding for
|
|
6
|
+
* validate, Issue for coherence) from ONE message builder, exactly as
|
|
7
|
+
* `../findings.ts` does for `capability.unknown`. Two shapes over one sentence
|
|
8
|
+
* is the only arrangement in which a finding an author reads at the service
|
|
9
|
+
* target and the refusal they hit at `loam archive` cannot describe the same
|
|
10
|
+
* breach differently.
|
|
11
|
+
*
|
|
12
|
+
* WHY THE DELTA SIDE GATES ARCHIVE. A delta requirement whose `Realizes:` entry
|
|
13
|
+
* resolves to nothing would merge a join pointing at a promise that does not
|
|
14
|
+
* exist, and the living document would then carry it looking exactly like a
|
|
15
|
+
* working join — the same argument that made `capability.unknown` a gate.
|
|
16
|
+
* `--approve` overrides it like any other error.
|
|
17
|
+
*/
|
|
18
|
+
import { compareIds } from "../../repo/entries.js";
|
|
19
|
+
import { resolveRealizes } from "./join.js";
|
|
20
|
+
/**
|
|
21
|
+
* The fix for one unresolved entry, as a sentence. Five arms, five different
|
|
22
|
+
* repairs — the whole reason `RealizesClaim` is not collapsed to a boolean.
|
|
23
|
+
*/
|
|
24
|
+
function advice(claim) {
|
|
25
|
+
switch (claim.kind) {
|
|
26
|
+
case "malformed":
|
|
27
|
+
return ("an entry names a capability requirement and is spelled `<capability-id>#<Requirement-ID>` — " +
|
|
28
|
+
"the capability half addresses the document, because a Requirement-ID is unique only inside its own. " +
|
|
29
|
+
"To claim a theme rather than a promise, use the `Capability:` line instead");
|
|
30
|
+
case "unknown-capability":
|
|
31
|
+
return claim.close.length > 0
|
|
32
|
+
? `no such capability is declared. Did you mean: ${claim.close.join(", ")}?`
|
|
33
|
+
: "no such capability is declared — declare it in architecture/capabilities.yaml, " +
|
|
34
|
+
`write capabilities/${claim.capability}/spec.md, or fix the spelling`;
|
|
35
|
+
case "undocumented-capability":
|
|
36
|
+
return (`capability '${claim.capability}' is declared but has no capabilities/${claim.capability}/spec.md, ` +
|
|
37
|
+
"so it carries no requirements to realize. Write the document — a name alone can be claimed with " +
|
|
38
|
+
"`Capability:`, but only a document can be realized");
|
|
39
|
+
case "empty-capability":
|
|
40
|
+
return (`capabilities/${claim.capability}/spec.md exists and declares no requirements yet — ` +
|
|
41
|
+
"add the `## Requirements` section, each requirement carrying its `Requirement-ID:`");
|
|
42
|
+
case "unknown-requirement":
|
|
43
|
+
return claim.close.length > 0
|
|
44
|
+
? `capabilities/${claim.capability}/spec.md declares no requirement with that id. Did you mean: ${claim.close.join(", ")}?`
|
|
45
|
+
: `capabilities/${claim.capability}/spec.md declares no requirement with that id — ` +
|
|
46
|
+
"check the `Requirement-ID:` lines in that document";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function unresolvedMessage(where, requirement, claim) {
|
|
50
|
+
return `${where}: requirement '${requirement}' — Realizes: '${claim.entry}' does not resolve: ${advice(claim)}`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Non-REMOVED requirements' entries that do not resolve — or nothing at all
|
|
54
|
+
* when the vocabulary cannot be graded against, which `resolveRealizes` answers
|
|
55
|
+
* by returning no claims.
|
|
56
|
+
*
|
|
57
|
+
* REMOVED is skipped for the reason its `Capability:` sibling skips it: a
|
|
58
|
+
* requirement on its way out of the living document is not making a claim about
|
|
59
|
+
* the future, and grading its joins would refuse the very change that removes
|
|
60
|
+
* the last consumer of a retired capability requirement.
|
|
61
|
+
*/
|
|
62
|
+
function unresolvedEntries(reqs, index) {
|
|
63
|
+
const out = [];
|
|
64
|
+
for (const r of reqs) {
|
|
65
|
+
if (r.kind === "REMOVED")
|
|
66
|
+
continue;
|
|
67
|
+
for (const claim of resolveRealizes(r.realizes, index)) {
|
|
68
|
+
if (claim.kind !== "resolved")
|
|
69
|
+
out.push({ requirement: r.name, claim });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* `capability.realizes-unknown` as validate sees it — an ERROR, the grade its
|
|
76
|
+
* two siblings already carry: a join that resolves to nothing reads exactly
|
|
77
|
+
* like a working one in the requirement, in the rollup and in every answer
|
|
78
|
+
* built over either.
|
|
79
|
+
*/
|
|
80
|
+
export function realizesUnknownFindings(reqs, target, index) {
|
|
81
|
+
return unresolvedEntries(reqs, index).map((u) => ({
|
|
82
|
+
severity: "error",
|
|
83
|
+
code: "capability.realizes-unknown",
|
|
84
|
+
subject: target.subject,
|
|
85
|
+
message: unresolvedMessage(target.where, u.requirement, u.claim),
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
/** The same rule shaped as coherence Issues, for a feature's delta documents — see the module comment. */
|
|
89
|
+
export function realizesUnknownIssues(reqs, target, index) {
|
|
90
|
+
return unresolvedEntries(reqs, index).map((u) => ({
|
|
91
|
+
severity: "error",
|
|
92
|
+
code: "capability.realizes-unknown",
|
|
93
|
+
subject: target.subject,
|
|
94
|
+
message: unresolvedMessage(target.where, u.requirement, u.claim),
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* `capability.realizes-stale` — ONE warn per pinned entry whose target has been
|
|
99
|
+
* rewritten since the pin was stamped.
|
|
100
|
+
*
|
|
101
|
+
* THE ONLY CHECK IN THE PRODUCT THAT NOTICES A LIVING DOCUMENT DECAYING. Every
|
|
102
|
+
* other join here is an EXISTENCE constraint: `capability.realizes-unknown`
|
|
103
|
+
* fires when the target is gone, and stays silent when the target is merely
|
|
104
|
+
* different. That silence is the failure this axis was missing — an analyst
|
|
105
|
+
* narrows a promise, and every service requirement claiming to keep it goes on
|
|
106
|
+
* claiming so, in a corpus loam rewrote itself, with nothing anywhere to say
|
|
107
|
+
* the claim was made about different words.
|
|
108
|
+
*
|
|
109
|
+
* WARN, AND IT MUST STAY A WARN. A pin going stale is not a defect: it is the
|
|
110
|
+
* fleet being told that a promise moved and somebody should re-read the
|
|
111
|
+
* requirements standing on it. The re-read is a human act with three legitimate
|
|
112
|
+
* outcomes — change the requirement, change the promise, or decide neither
|
|
113
|
+
* needed to change — and only the third is `loam rebase --living` on its own.
|
|
114
|
+
* An error would make the middle outcome unavailable without `--approve`, and a
|
|
115
|
+
* gate would make an analyst's ordinary edit break other teams' builds.
|
|
116
|
+
*
|
|
117
|
+
* Unpinned entries produce nothing, forever. A corpus that has never been
|
|
118
|
+
* pinned grades exactly as it did before pins existed, which is what lets this
|
|
119
|
+
* land without a migration.
|
|
120
|
+
*/
|
|
121
|
+
export function realizesStaleFindings(reqs, target, index) {
|
|
122
|
+
const stale = [];
|
|
123
|
+
for (const r of reqs) {
|
|
124
|
+
if (r.kind === "REMOVED")
|
|
125
|
+
continue;
|
|
126
|
+
for (const claim of resolveRealizes(r.realizes, index)) {
|
|
127
|
+
if (claim.kind !== "resolved" || claim.stale !== true)
|
|
128
|
+
continue;
|
|
129
|
+
stale.push({
|
|
130
|
+
requirement: r.name,
|
|
131
|
+
entry: claim.entry,
|
|
132
|
+
capability: claim.capability,
|
|
133
|
+
target: claim.requirement,
|
|
134
|
+
pin: claim.pin,
|
|
135
|
+
current: claim.current,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return stale.map((s) => ({
|
|
140
|
+
severity: "warn",
|
|
141
|
+
code: "capability.realizes-stale",
|
|
142
|
+
subject: target.subject,
|
|
143
|
+
message: `${target.where}: requirement '${s.requirement}' realizes ${s.capability}#${s.target}, ` +
|
|
144
|
+
`which has been rewritten since this claim was pinned (pinned ${s.pin}, now ${s.current}). ` +
|
|
145
|
+
"The join still resolves — what changed is the promise, not its address. " +
|
|
146
|
+
`Re-read capabilities/${s.capability}/spec.md and decide which side is now wrong; ` +
|
|
147
|
+
"when this requirement is right as it stands, `loam rebase --living` re-pins it",
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* `capability.requirement-unrealized` — ONE warn per capability requirement no
|
|
152
|
+
* living service requirement's `Realizes:` line names.
|
|
153
|
+
*
|
|
154
|
+
* The sharper half of the axis, and the reason the join was worth building.
|
|
155
|
+
* `capability.unrealized` already says a whole capability is claimed by nobody,
|
|
156
|
+
* which is the loud case somebody notices; this says the fleet claims the
|
|
157
|
+
* capability and has left one named promise inside it unimplemented — the case
|
|
158
|
+
* that survives every rollup, because the capability's OTHER requirements make
|
|
159
|
+
* its row look healthy.
|
|
160
|
+
*
|
|
161
|
+
* Warn rather than error, and per requirement rather than one rollup finding
|
|
162
|
+
* with details[]: an analyst writing next quarter's promises before anybody
|
|
163
|
+
* implements them is the normal, intended use of the document, and a corpus
|
|
164
|
+
* that fails the gate the moment it is written ahead of the fleet is a corpus
|
|
165
|
+
* nobody writes ahead. Subject is `<capability>#<id>` — the entry an author
|
|
166
|
+
* would type to fix it, which is the string they should be able to copy.
|
|
167
|
+
*/
|
|
168
|
+
export function requirementUnrealizedFindings(unrealized) {
|
|
169
|
+
return [...unrealized]
|
|
170
|
+
.sort((a, b) => compareIds(a.capability, b.capability) || compareIds(a.id, b.id))
|
|
171
|
+
.map((u) => ({
|
|
172
|
+
severity: "warn",
|
|
173
|
+
code: "capability.requirement-unrealized",
|
|
174
|
+
subject: `${u.capability}#${u.id}`,
|
|
175
|
+
message: `landscape: capabilities/${u.capability}/spec.md — '${u.name}' (${u.id}) is realized by no living requirement: ` +
|
|
176
|
+
`no service's \`Realizes:\` line names ${u.capability}#${u.id}. ` +
|
|
177
|
+
"Either the promise is not implemented yet, or the requirements that implement it have not said so — " +
|
|
178
|
+
"add the line to each service requirement that carries part of it",
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=findings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findings.js","sourceRoot":"","sources":["../../../../src/core/capabilities/realizes/findings.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The `Realizes:` grades, both directions: an entry that resolves to nothing,\n * and a capability requirement nothing realizes.\n *\n * Values only — core never prints — and the join rule shaped twice (Finding for\n * validate, Issue for coherence) from ONE message builder, exactly as\n * `../findings.ts` does for `capability.unknown`. Two shapes over one sentence\n * is the only arrangement in which a finding an author reads at the service\n * target and the refusal they hit at `loam archive` cannot describe the same\n * breach differently.\n *\n * WHY THE DELTA SIDE GATES ARCHIVE. A delta requirement whose `Realizes:` entry\n * resolves to nothing would merge a join pointing at a promise that does not\n * exist, and the living document would then carry it looking exactly like a\n * working join — the same argument that made `capability.unknown` a gate.\n * `--approve` overrides it like any other error.\n */\nimport { compareIds } from \"../../repo/entries.js\";\nimport type { Requirement } from \"../../document/spec.js\";\nimport type { Finding } from \"../../vocabulary/report.js\";\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport { resolveRealizes, type CapabilityRequirementIndex, type RealizesClaim } from \"./join.js\";\n\n/** Which document is being graded, and under whose name the finding is filed. */\nexport interface RealizesTargetDoc {\n where: string;\n subject: string;\n}\n\n/**\n * An entry that did NOT resolve. Spelled as an exclusion rather than as a\n * second union so the arms cannot drift: adding a failure kind to\n * `RealizesClaim` makes `advice` below fail to compile until it says what to do\n * about it, which is exactly the reminder a new failure mode deserves.\n */\ntype UnresolvedClaim = Exclude<RealizesClaim, { kind: \"resolved\" }>;\n\n/**\n * The fix for one unresolved entry, as a sentence. Five arms, five different\n * repairs — the whole reason `RealizesClaim` is not collapsed to a boolean.\n */\nfunction advice(claim: UnresolvedClaim): string {\n switch (claim.kind) {\n case \"malformed\":\n return (\n \"an entry names a capability requirement and is spelled `<capability-id>#<Requirement-ID>` — \" +\n \"the capability half addresses the document, because a Requirement-ID is unique only inside its own. \" +\n \"To claim a theme rather than a promise, use the `Capability:` line instead\"\n );\n case \"unknown-capability\":\n return claim.close.length > 0\n ? `no such capability is declared. Did you mean: ${claim.close.join(\", \")}?`\n : \"no such capability is declared — declare it in architecture/capabilities.yaml, \" +\n `write capabilities/${claim.capability}/spec.md, or fix the spelling`;\n case \"undocumented-capability\":\n return (\n `capability '${claim.capability}' is declared but has no capabilities/${claim.capability}/spec.md, ` +\n \"so it carries no requirements to realize. Write the document — a name alone can be claimed with \" +\n \"`Capability:`, but only a document can be realized\"\n );\n case \"empty-capability\":\n return (\n `capabilities/${claim.capability}/spec.md exists and declares no requirements yet — ` +\n \"add the `## Requirements` section, each requirement carrying its `Requirement-ID:`\"\n );\n case \"unknown-requirement\":\n return claim.close.length > 0\n ? `capabilities/${claim.capability}/spec.md declares no requirement with that id. Did you mean: ${claim.close.join(\", \")}?`\n : `capabilities/${claim.capability}/spec.md declares no requirement with that id — ` +\n \"check the `Requirement-ID:` lines in that document\";\n }\n}\n\nfunction unresolvedMessage(where: string, requirement: string, claim: UnresolvedClaim): string {\n return `${where}: requirement '${requirement}' — Realizes: '${claim.entry}' does not resolve: ${advice(claim)}`;\n}\n\n/** One unresolved entry, with the requirement that wrote it. */\ninterface Unresolved {\n requirement: string;\n claim: UnresolvedClaim;\n}\n\n/**\n * Non-REMOVED requirements' entries that do not resolve — or nothing at all\n * when the vocabulary cannot be graded against, which `resolveRealizes` answers\n * by returning no claims.\n *\n * REMOVED is skipped for the reason its `Capability:` sibling skips it: a\n * requirement on its way out of the living document is not making a claim about\n * the future, and grading its joins would refuse the very change that removes\n * the last consumer of a retired capability requirement.\n */\nfunction unresolvedEntries(reqs: Requirement[], index: CapabilityRequirementIndex): Unresolved[] {\n const out: Unresolved[] = [];\n for (const r of reqs) {\n if (r.kind === \"REMOVED\") continue;\n for (const claim of resolveRealizes(r.realizes, index)) {\n if (claim.kind !== \"resolved\") out.push({ requirement: r.name, claim });\n }\n }\n return out;\n}\n\n/**\n * `capability.realizes-unknown` as validate sees it — an ERROR, the grade its\n * two siblings already carry: a join that resolves to nothing reads exactly\n * like a working one in the requirement, in the rollup and in every answer\n * built over either.\n */\nexport function realizesUnknownFindings(\n reqs: Requirement[],\n target: RealizesTargetDoc,\n index: CapabilityRequirementIndex,\n): Finding[] {\n return unresolvedEntries(reqs, index).map((u) => ({\n severity: \"error\" as const,\n code: \"capability.realizes-unknown\",\n subject: target.subject,\n message: unresolvedMessage(target.where, u.requirement, u.claim),\n }));\n}\n\n/** The same rule shaped as coherence Issues, for a feature's delta documents — see the module comment. */\nexport function realizesUnknownIssues(\n reqs: Requirement[],\n target: RealizesTargetDoc,\n index: CapabilityRequirementIndex,\n): Issue[] {\n return unresolvedEntries(reqs, index).map((u) => ({\n severity: \"error\" as const,\n code: \"capability.realizes-unknown\" as const,\n subject: target.subject,\n message: unresolvedMessage(target.where, u.requirement, u.claim),\n }));\n}\n\n/** One resolved entry whose pin no longer matches the promise it points at. */\ninterface StaleEntry {\n requirement: string;\n entry: string;\n capability: string;\n target: string;\n pin: string;\n current: string;\n}\n\n/**\n * `capability.realizes-stale` — ONE warn per pinned entry whose target has been\n * rewritten since the pin was stamped.\n *\n * THE ONLY CHECK IN THE PRODUCT THAT NOTICES A LIVING DOCUMENT DECAYING. Every\n * other join here is an EXISTENCE constraint: `capability.realizes-unknown`\n * fires when the target is gone, and stays silent when the target is merely\n * different. That silence is the failure this axis was missing — an analyst\n * narrows a promise, and every service requirement claiming to keep it goes on\n * claiming so, in a corpus loam rewrote itself, with nothing anywhere to say\n * the claim was made about different words.\n *\n * WARN, AND IT MUST STAY A WARN. A pin going stale is not a defect: it is the\n * fleet being told that a promise moved and somebody should re-read the\n * requirements standing on it. The re-read is a human act with three legitimate\n * outcomes — change the requirement, change the promise, or decide neither\n * needed to change — and only the third is `loam rebase --living` on its own.\n * An error would make the middle outcome unavailable without `--approve`, and a\n * gate would make an analyst's ordinary edit break other teams' builds.\n *\n * Unpinned entries produce nothing, forever. A corpus that has never been\n * pinned grades exactly as it did before pins existed, which is what lets this\n * land without a migration.\n */\nexport function realizesStaleFindings(\n reqs: Requirement[],\n target: RealizesTargetDoc,\n index: CapabilityRequirementIndex,\n): Finding[] {\n const stale: StaleEntry[] = [];\n for (const r of reqs) {\n if (r.kind === \"REMOVED\") continue;\n for (const claim of resolveRealizes(r.realizes, index)) {\n if (claim.kind !== \"resolved\" || claim.stale !== true) continue;\n stale.push({\n requirement: r.name,\n entry: claim.entry,\n capability: claim.capability,\n target: claim.requirement,\n pin: claim.pin!,\n current: claim.current!,\n });\n }\n }\n return stale.map((s) => ({\n severity: \"warn\" as const,\n code: \"capability.realizes-stale\",\n subject: target.subject,\n message:\n `${target.where}: requirement '${s.requirement}' realizes ${s.capability}#${s.target}, ` +\n `which has been rewritten since this claim was pinned (pinned ${s.pin}, now ${s.current}). ` +\n \"The join still resolves — what changed is the promise, not its address. \" +\n `Re-read capabilities/${s.capability}/spec.md and decide which side is now wrong; ` +\n \"when this requirement is right as it stands, `loam rebase --living` re-pins it\",\n }));\n}\n\n/** One capability requirement nothing realizes, located exactly. */\nexport interface UnrealizedRequirement {\n capability: string;\n /** Its `Requirement-ID:`. */\n id: string;\n /** Its `### Requirement:` heading, for a reader who has the document open. */\n name: string;\n}\n\n/**\n * `capability.requirement-unrealized` — ONE warn per capability requirement no\n * living service requirement's `Realizes:` line names.\n *\n * The sharper half of the axis, and the reason the join was worth building.\n * `capability.unrealized` already says a whole capability is claimed by nobody,\n * which is the loud case somebody notices; this says the fleet claims the\n * capability and has left one named promise inside it unimplemented — the case\n * that survives every rollup, because the capability's OTHER requirements make\n * its row look healthy.\n *\n * Warn rather than error, and per requirement rather than one rollup finding\n * with details[]: an analyst writing next quarter's promises before anybody\n * implements them is the normal, intended use of the document, and a corpus\n * that fails the gate the moment it is written ahead of the fleet is a corpus\n * nobody writes ahead. Subject is `<capability>#<id>` — the entry an author\n * would type to fix it, which is the string they should be able to copy.\n */\nexport function requirementUnrealizedFindings(unrealized: UnrealizedRequirement[]): Finding[] {\n return [...unrealized]\n .sort((a, b) => compareIds(a.capability, b.capability) || compareIds(a.id, b.id))\n .map((u) => ({\n severity: \"warn\" as const,\n code: \"capability.requirement-unrealized\",\n subject: `${u.capability}#${u.id}`,\n message:\n `landscape: capabilities/${u.capability}/spec.md — '${u.name}' (${u.id}) is realized by no living requirement: ` +\n `no service's \\`Realizes:\\` line names ${u.capability}#${u.id}. ` +\n \"Either the promise is not implemented yet, or the requirements that implement it have not said so — \" +\n \"add the line to each service requirement that carries part of it\",\n }));\n}\n"]}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The join from a service requirement INTO the authored business tree:
|
|
3
|
+
* `Realizes: <capability-id>#<Requirement-ID>`.
|
|
4
|
+
*
|
|
5
|
+
* This is the axis's load-bearing edge. `Capability:` beside it answers a
|
|
6
|
+
* different question and neither replaces the other — `Capability: checkout`
|
|
7
|
+
* claims a theme, `Realizes: checkout#CHECKOUT-CHARGE-ONCE` claims that this
|
|
8
|
+
* requirement is part of what makes one named promise true. Only the second can
|
|
9
|
+
* be graded in both directions, and both directions are the point: an entry
|
|
10
|
+
* that resolves to nothing, and a capability requirement nothing realizes.
|
|
11
|
+
*
|
|
12
|
+
* WHO WRITES IT. Not the analyst. The analyst writes the capability and its
|
|
13
|
+
* requirements and names no service; whoever implements a service requirement
|
|
14
|
+
* writes the line that says which promise it serves. That is why the line lives
|
|
15
|
+
* on the SERVICE requirement and not as a back-reference in the capability
|
|
16
|
+
* document: a business document must not have to be edited every time a fleet
|
|
17
|
+
* rearranges which service carries which part.
|
|
18
|
+
*
|
|
19
|
+
* WHY NO SEPARATE LINE FOR THE API HOP. A capability requirement reaches its
|
|
20
|
+
* operations by composing this join with the `Operations:` lines that already
|
|
21
|
+
* exist on the requirements realizing it. A third place to keep in sync would
|
|
22
|
+
* buy nothing and could disagree with the two.
|
|
23
|
+
*
|
|
24
|
+
* DELIBERATELY THE NARROWEST LAYER THAT CAN MEAN IT, exactly as
|
|
25
|
+
* `../usecase-join.ts` is: no file is read, no finding is emitted, nothing is
|
|
26
|
+
* thrown, and — the load-bearing part — this package does NOT import
|
|
27
|
+
* `../capabilities.js`. It takes ids somebody else supplies. That keeps the
|
|
28
|
+
* package edge one-way (`capabilities` → `capabilities/realizes`), and it keeps
|
|
29
|
+
* the resolution testable without a filesystem.
|
|
30
|
+
*
|
|
31
|
+
* WHAT THE CALLER STILL OWES. The vocabulary ladder in `../findings.ts` — an
|
|
32
|
+
* absent or unreadable `architecture/capabilities.yaml` means silence, not a
|
|
33
|
+
* fleet full of unresolved joins — is applied BEFORE calling here, and travels
|
|
34
|
+
* as `declared: null`.
|
|
35
|
+
*/
|
|
36
|
+
import { closeIds } from "../../c4/arch.js";
|
|
37
|
+
import { splitRealizesPin } from "../../document/spec.js";
|
|
38
|
+
import { compareIds } from "../../repo/entries.js";
|
|
39
|
+
/**
|
|
40
|
+
* The separator between the two halves of an entry.
|
|
41
|
+
*
|
|
42
|
+
* A composite entry rather than two lines because a `Requirement-ID` is unique
|
|
43
|
+
* only inside its own document: the capability half is not decoration, it is
|
|
44
|
+
* what makes the target addressable. Two lines would let one requirement name
|
|
45
|
+
* three capabilities and four ids with nothing saying which belongs to which.
|
|
46
|
+
*/
|
|
47
|
+
export const REALIZES_SEPARATOR = "#";
|
|
48
|
+
/**
|
|
49
|
+
* Split one entry, or `null` when it has no two halves.
|
|
50
|
+
*
|
|
51
|
+
* THE LAST SEPARATOR, NOT THE FIRST, and the asymmetry is the reason: the
|
|
52
|
+
* requirement half has a strict grammar (`REQUIREMENT_ID_RE` in
|
|
53
|
+
* `core/document/spec.ts`) that excludes `#`, while the capability half is a
|
|
54
|
+
* YAML key and a directory name and is constrained nowhere. Splitting at the
|
|
55
|
+
* last `#` is therefore unambiguous for every capability id there is;
|
|
56
|
+
* splitting at the first would mis-parse any id containing one, and would do it
|
|
57
|
+
* silently — the failure mode this whole axis exists to refuse.
|
|
58
|
+
*
|
|
59
|
+
* Both halves must be non-empty. `checkout#`, `#CHK-1` and a bare `checkout`
|
|
60
|
+
* are all one answer — `null` — because the fix for all three is the same
|
|
61
|
+
* sentence, and inventing three refusals for one mistake helps nobody.
|
|
62
|
+
*/
|
|
63
|
+
export function splitRealizesEntry(entry) {
|
|
64
|
+
// The pin comes off FIRST. It is a suffix of the whole entry, and leaving it
|
|
65
|
+
// on would put `@<digest>` inside the requirement half, where it would fail
|
|
66
|
+
// to resolve against an id that never contained one.
|
|
67
|
+
const { target, pin } = splitRealizesPin(entry);
|
|
68
|
+
const at = target.lastIndexOf(REALIZES_SEPARATOR);
|
|
69
|
+
if (at <= 0 || at === target.length - 1)
|
|
70
|
+
return null;
|
|
71
|
+
return { capability: target.slice(0, at), requirement: target.slice(at + 1), pin };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Resolve every entry, in the order the author wrote them.
|
|
75
|
+
*
|
|
76
|
+
* Order preserved and repeats NOT collapsed, matching the `Capability:` line's
|
|
77
|
+
* behaviour in `../findings.ts` rather than the `#cap-` tag's in
|
|
78
|
+
* `../usecase-join.ts`. The two siblings differ because a tag set is a set —
|
|
79
|
+
* LikeC4 hands it over with no authored order to preserve — while a body line
|
|
80
|
+
* is text somebody typed, and a reader chasing a finding is looking at that
|
|
81
|
+
* text. Consistency with the line beside it is worth more here than a
|
|
82
|
+
* deduplication nobody asked for.
|
|
83
|
+
*
|
|
84
|
+
* An empty result when `declared` is `null` is the ladder, not an answer: there
|
|
85
|
+
* is nothing to grade against, so nothing is graded.
|
|
86
|
+
*/
|
|
87
|
+
export function resolveRealizes(entries, index) {
|
|
88
|
+
const { declared, byCapability, digests } = index;
|
|
89
|
+
if (declared === null)
|
|
90
|
+
return [];
|
|
91
|
+
const known = new Set(declared);
|
|
92
|
+
return entries.map((entry) => {
|
|
93
|
+
const target = splitRealizesEntry(entry);
|
|
94
|
+
if (target === null)
|
|
95
|
+
return { entry, kind: "malformed" };
|
|
96
|
+
const { capability, requirement, pin } = target;
|
|
97
|
+
if (!known.has(capability)) {
|
|
98
|
+
return { entry, kind: "unknown-capability", capability, close: closeIds(capability, declared) };
|
|
99
|
+
}
|
|
100
|
+
const ids = byCapability.get(capability);
|
|
101
|
+
if (ids === undefined)
|
|
102
|
+
return { entry, kind: "undocumented-capability", capability };
|
|
103
|
+
if (ids.size === 0)
|
|
104
|
+
return { entry, kind: "empty-capability", capability };
|
|
105
|
+
if (ids.has(requirement)) {
|
|
106
|
+
// An index with no digest for a resolved id leaves the verdict UNKNOWN
|
|
107
|
+
// rather than STALE. The two are not the same claim, and a reader who is
|
|
108
|
+
// told a pin went stale because the grader could not compute the other
|
|
109
|
+
// side would be told something false.
|
|
110
|
+
const current = digests.get(capability)?.get(requirement) ?? null;
|
|
111
|
+
const stale = pin === null || current === null ? null : pin !== current;
|
|
112
|
+
return { entry, kind: "resolved", capability, requirement, pin, current, stale };
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
entry,
|
|
116
|
+
kind: "unknown-requirement",
|
|
117
|
+
capability,
|
|
118
|
+
requirement,
|
|
119
|
+
close: closeIds(requirement, [...ids].sort(compareIds)),
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The capabilities an entry list CLAIMS, whether or not each entry resolves.
|
|
125
|
+
*
|
|
126
|
+
* Used to widen the set `capability.unrealized` is taken against, and the
|
|
127
|
+
* widening is a correctness fix rather than a convenience: realizing a
|
|
128
|
+
* capability's requirement IS realizing part of that capability, so an author
|
|
129
|
+
* who wrote only `Realizes:` lines would otherwise be warned that a capability
|
|
130
|
+
* demonstrably realized by their fleet is realized by nobody.
|
|
131
|
+
*
|
|
132
|
+
* Claims from UNRESOLVED entries count too, and that is deliberate. The entry
|
|
133
|
+
* already earns `capability.realizes-unknown`; letting it also produce a
|
|
134
|
+
* spurious `capability.unrealized` would be two findings for one mistake, and
|
|
135
|
+
* the second would point at the analyst's document rather than at the typo.
|
|
136
|
+
*/
|
|
137
|
+
export function capabilitiesClaimedByRealizes(entries) {
|
|
138
|
+
return entries.flatMap((entry) => {
|
|
139
|
+
const target = splitRealizesEntry(entry);
|
|
140
|
+
return target === null ? [] : [target.capability];
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=join.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"join.js","sourceRoot":"","sources":["../../../../src/core/capabilities/realizes/join.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The join from a service requirement INTO the authored business tree:\n * `Realizes: <capability-id>#<Requirement-ID>`.\n *\n * This is the axis's load-bearing edge. `Capability:` beside it answers a\n * different question and neither replaces the other — `Capability: checkout`\n * claims a theme, `Realizes: checkout#CHECKOUT-CHARGE-ONCE` claims that this\n * requirement is part of what makes one named promise true. Only the second can\n * be graded in both directions, and both directions are the point: an entry\n * that resolves to nothing, and a capability requirement nothing realizes.\n *\n * WHO WRITES IT. Not the analyst. The analyst writes the capability and its\n * requirements and names no service; whoever implements a service requirement\n * writes the line that says which promise it serves. That is why the line lives\n * on the SERVICE requirement and not as a back-reference in the capability\n * document: a business document must not have to be edited every time a fleet\n * rearranges which service carries which part.\n *\n * WHY NO SEPARATE LINE FOR THE API HOP. A capability requirement reaches its\n * operations by composing this join with the `Operations:` lines that already\n * exist on the requirements realizing it. A third place to keep in sync would\n * buy nothing and could disagree with the two.\n *\n * DELIBERATELY THE NARROWEST LAYER THAT CAN MEAN IT, exactly as\n * `../usecase-join.ts` is: no file is read, no finding is emitted, nothing is\n * thrown, and — the load-bearing part — this package does NOT import\n * `../capabilities.js`. It takes ids somebody else supplies. That keeps the\n * package edge one-way (`capabilities` → `capabilities/realizes`), and it keeps\n * the resolution testable without a filesystem.\n *\n * WHAT THE CALLER STILL OWES. The vocabulary ladder in `../findings.ts` — an\n * absent or unreadable `architecture/capabilities.yaml` means silence, not a\n * fleet full of unresolved joins — is applied BEFORE calling here, and travels\n * as `declared: null`.\n */\nimport { closeIds } from \"../../c4/arch.js\";\nimport { splitRealizesPin } from \"../../document/spec.js\";\nimport { compareIds } from \"../../repo/entries.js\";\n\n/**\n * The separator between the two halves of an entry.\n *\n * A composite entry rather than two lines because a `Requirement-ID` is unique\n * only inside its own document: the capability half is not decoration, it is\n * what makes the target addressable. Two lines would let one requirement name\n * three capabilities and four ids with nothing saying which belongs to which.\n */\nexport const REALIZES_SEPARATOR = \"#\";\n\n/** The two halves of one entry, plus the version it was written against. */\nexport interface RealizesTarget {\n capability: string;\n requirement: string;\n /**\n * The digest of the capability requirement this claim was last pinned to, or\n * `null` for an unpinned entry. Written by `loam rebase --living`, never by\n * hand; `null` is the normal state of a corpus that has not been pinned and\n * grades exactly as it did before pins existed.\n */\n pin: string | null;\n}\n\n/**\n * Split one entry, or `null` when it has no two halves.\n *\n * THE LAST SEPARATOR, NOT THE FIRST, and the asymmetry is the reason: the\n * requirement half has a strict grammar (`REQUIREMENT_ID_RE` in\n * `core/document/spec.ts`) that excludes `#`, while the capability half is a\n * YAML key and a directory name and is constrained nowhere. Splitting at the\n * last `#` is therefore unambiguous for every capability id there is;\n * splitting at the first would mis-parse any id containing one, and would do it\n * silently — the failure mode this whole axis exists to refuse.\n *\n * Both halves must be non-empty. `checkout#`, `#CHK-1` and a bare `checkout`\n * are all one answer — `null` — because the fix for all three is the same\n * sentence, and inventing three refusals for one mistake helps nobody.\n */\nexport function splitRealizesEntry(entry: string): RealizesTarget | null {\n // The pin comes off FIRST. It is a suffix of the whole entry, and leaving it\n // on would put `@<digest>` inside the requirement half, where it would fail\n // to resolve against an id that never contained one.\n const { target, pin } = splitRealizesPin(entry);\n const at = target.lastIndexOf(REALIZES_SEPARATOR);\n if (at <= 0 || at === target.length - 1) return null;\n return { capability: target.slice(0, at), requirement: target.slice(at + 1), pin };\n}\n\n/**\n * What the tree declares, as the resolution needs it.\n *\n * The two fields answer different questions and both are needed to tell the\n * failures apart: `declared` is every capability id the fleet knows — the union\n * of the YAML and the tree — while `byCapability` holds only those with a\n * document, since only a document can carry requirements. A capability in\n * `declared` and absent from `byCapability` is declared-but-undocumented, which\n * is a real and common mid-adoption state with a fix of its own.\n */\nexport interface CapabilityRequirementIndex {\n /**\n * Every declared capability id, or `null` when the vocabulary cannot be\n * graded against at all. `null` and `[]` are never interchangeable — the same\n * distinction `gradableCapabilityIds` draws, for the same reason.\n */\n declared: readonly string[] | null;\n /** Requirement ids per capability that HAS a document. Present-and-empty is a real answer. */\n byCapability: ReadonlyMap<string, ReadonlySet<string>>;\n /**\n * The current `requirementDigest` of each id in `byCapability`, for the pin\n * comparison and nothing else.\n *\n * A second collection over the same keys, which is a shape this codebase\n * otherwise refuses — the justification is that the alternative is worse.\n * Widening `byCapability`'s values to a Map would change what `[...ids]`\n * iterates at four call sites that legitimately want the ids alone, and a\n * silent change of meaning in an existing iteration is a far more expensive\n * mistake than a redundant map built in the same loop. The invariant — same\n * capabilities, same ids, both sides — is asserted by\n * `test/capability-realizes-pin.test.ts` rather than left to a comment.\n */\n digests: ReadonlyMap<string, ReadonlyMap<string, string>>;\n}\n\n/**\n * What one `Realizes:` entry joins to. Branch on `kind`; every arm carries the\n * entry it is about, so a message can name it without re-deriving it.\n *\n * Five arms because there are five different fixes. Collapsing them would be\n * cheaper to write and worse to read: \"does not resolve\" tells an author\n * nothing about whether to declare a capability, write a document, add a\n * requirements section, or correct four characters.\n */\nexport type RealizesClaim =\n /**\n * Both halves resolve. `stale` is the pin verdict, and it is a THIRD state\n * rather than a boolean: `null` means the entry carries no pin, which is not\n * the same answer as a pin that still matches. Only `true` is a finding.\n */\n | {\n entry: string;\n kind: \"resolved\";\n capability: string;\n requirement: string;\n pin: string | null;\n /** The target's digest now, or `null` when the index could not supply one. */\n current: string | null;\n stale: boolean | null;\n }\n /** No usable separator — the fix is the spelling of the entry itself. */\n | { entry: string; kind: \"malformed\" }\n /** Nothing declares the capability half. `close` holds real declared ids. */\n | { entry: string; kind: \"unknown-capability\"; capability: string; close: string[] }\n /** Declared, but with no `capabilities/<id>/spec.md` — so it has no requirements at all. */\n | { entry: string; kind: \"undocumented-capability\"; capability: string }\n /** The document exists and declares no requirements yet. */\n | { entry: string; kind: \"empty-capability\"; capability: string }\n /** The document exists and declares requirements, but not this one. */\n | { entry: string; kind: \"unknown-requirement\"; capability: string; requirement: string; close: string[] };\n\n/**\n * Resolve every entry, in the order the author wrote them.\n *\n * Order preserved and repeats NOT collapsed, matching the `Capability:` line's\n * behaviour in `../findings.ts` rather than the `#cap-` tag's in\n * `../usecase-join.ts`. The two siblings differ because a tag set is a set —\n * LikeC4 hands it over with no authored order to preserve — while a body line\n * is text somebody typed, and a reader chasing a finding is looking at that\n * text. Consistency with the line beside it is worth more here than a\n * deduplication nobody asked for.\n *\n * An empty result when `declared` is `null` is the ladder, not an answer: there\n * is nothing to grade against, so nothing is graded.\n */\nexport function resolveRealizes(\n entries: readonly string[],\n index: CapabilityRequirementIndex,\n): RealizesClaim[] {\n const { declared, byCapability, digests } = index;\n if (declared === null) return [];\n const known = new Set(declared);\n\n return entries.map((entry): RealizesClaim => {\n const target = splitRealizesEntry(entry);\n if (target === null) return { entry, kind: \"malformed\" };\n const { capability, requirement, pin } = target;\n if (!known.has(capability)) {\n return { entry, kind: \"unknown-capability\", capability, close: closeIds(capability, declared) };\n }\n const ids = byCapability.get(capability);\n if (ids === undefined) return { entry, kind: \"undocumented-capability\", capability };\n if (ids.size === 0) return { entry, kind: \"empty-capability\", capability };\n if (ids.has(requirement)) {\n // An index with no digest for a resolved id leaves the verdict UNKNOWN\n // rather than STALE. The two are not the same claim, and a reader who is\n // told a pin went stale because the grader could not compute the other\n // side would be told something false.\n const current = digests.get(capability)?.get(requirement) ?? null;\n const stale = pin === null || current === null ? null : pin !== current;\n return { entry, kind: \"resolved\", capability, requirement, pin, current, stale };\n }\n return {\n entry,\n kind: \"unknown-requirement\",\n capability,\n requirement,\n close: closeIds(requirement, [...ids].sort(compareIds)),\n };\n });\n}\n\n/**\n * The capabilities an entry list CLAIMS, whether or not each entry resolves.\n *\n * Used to widen the set `capability.unrealized` is taken against, and the\n * widening is a correctness fix rather than a convenience: realizing a\n * capability's requirement IS realizing part of that capability, so an author\n * who wrote only `Realizes:` lines would otherwise be warned that a capability\n * demonstrably realized by their fleet is realized by nobody.\n *\n * Claims from UNRESOLVED entries count too, and that is deliberate. The entry\n * already earns `capability.realizes-unknown`; letting it also produce a\n * spurious `capability.unrealized` would be two findings for one mistake, and\n * the second would point at the analyst's document rather than at the typo.\n */\nexport function capabilitiesClaimedByRealizes(entries: readonly string[]): string[] {\n return entries.flatMap((entry) => {\n const target = splitRealizesEntry(entry);\n return target === null ? [] : [target.capability];\n });\n}\n"]}
|