@ybotok/loam 0.1.0-beta.3 → 0.2.0-alpha.13
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 +5992 -82
- package/COMPARISON.md +168 -63
- package/CONTRIBUTING.md +121 -0
- package/MIGRATING-from-OpenSpec.md +238 -55
- package/QUICKSTART.md +302 -0
- package/README.md +653 -144
- package/ROADMAP.md +1035 -0
- package/SCHEMA.md +3916 -263
- package/SECURITY.md +21 -3
- package/WORKFLOW.md +927 -0
- package/dist/cli.js +133 -21
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +204 -0
- package/dist/commands/adopt/adopt.js.map +1 -0
- package/dist/commands/adopt/render.js +309 -0
- package/dist/commands/adopt/render.js.map +1 -0
- package/dist/commands/archive/archive.js +74 -0
- package/dist/commands/archive/archive.js.map +1 -0
- package/dist/commands/archive/closing.js +42 -0
- package/dist/commands/archive/closing.js.map +1 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js +172 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/consumers.js +56 -0
- package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +193 -0
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
- package/dist/commands/archive/plan/document/head.js +151 -0
- package/dist/commands/archive/plan/document/head.js.map +1 -0
- package/dist/commands/archive/plan/document/links.js +284 -0
- package/dist/commands/archive/plan/document/links.js.map +1 -0
- package/dist/commands/archive/plan/document/run.js +136 -0
- package/dist/commands/archive/plan/document/run.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +319 -0
- package/dist/commands/archive/plan/gate.js.map +1 -0
- package/dist/commands/archive/plan/landscape.js +298 -0
- package/dist/commands/archive/plan/landscape.js.map +1 -0
- package/dist/commands/archive/plan/model/extending.js +85 -0
- package/dist/commands/archive/plan/model/extending.js.map +1 -0
- package/dist/commands/archive/plan/refusal.js +106 -0
- package/dist/commands/archive/plan/refusal.js.map +1 -0
- package/dist/commands/archive/plan/specs.js +318 -0
- package/dist/commands/archive/plan/specs.js.map +1 -0
- package/dist/commands/archive/plan/state.js +30 -0
- package/dist/commands/archive/plan/state.js.map +1 -0
- package/dist/commands/archive/run.js +355 -0
- package/dist/commands/archive/run.js.map +1 -0
- package/dist/commands/archive/shipped.js +49 -0
- package/dist/commands/archive/shipped.js.map +1 -0
- package/dist/commands/context/context.js +166 -0
- package/dist/commands/context/context.js.map +1 -0
- package/dist/commands/context/print.js +305 -0
- package/dist/commands/context/print.js.map +1 -0
- package/dist/commands/delta/delta.js +367 -0
- package/dist/commands/delta/delta.js.map +1 -0
- package/dist/commands/delta/print.js +221 -0
- package/dist/commands/delta/print.js.map +1 -0
- package/dist/commands/dependencies.js +6 -8
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +86 -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 +65 -5
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain/explain.js +227 -0
- package/dist/commands/explain/explain.js.map +1 -0
- package/dist/commands/explore.js +39 -28
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +152 -0
- package/dist/commands/gate/gate.js.map +1 -0
- package/dist/commands/gate/print.js +66 -0
- package/dist/commands/gate/print.js.map +1 -0
- package/dist/commands/gherkin/commit.js +105 -0
- package/dist/commands/gherkin/commit.js.map +1 -0
- package/dist/commands/gherkin/gherkin.js +262 -0
- package/dist/commands/gherkin/gherkin.js.map +1 -0
- package/dist/commands/gherkin/reconcile.js +146 -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 +72 -0
- package/dist/commands/init/first-hour.js.map +1 -0
- package/dist/commands/{init.js → init/init.js} +142 -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 +63 -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 +155 -0
- package/dist/commands/list/json.js.map +1 -0
- package/dist/commands/list/list.js +358 -0
- package/dist/commands/list/list.js.map +1 -0
- package/dist/commands/list/print.js +344 -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/suites/suites.js +140 -0
- package/dist/commands/list/suites/suites.js.map +1 -0
- package/dist/commands/list/views.js +108 -0
- package/dist/commands/list/views.js.map +1 -0
- package/dist/commands/mcp/dispatch.js +238 -0
- package/dist/commands/mcp/dispatch.js.map +1 -0
- package/dist/commands/mcp/mcp.js +75 -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 +175 -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 +143 -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 +86 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/error.js +18 -0
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js +162 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/paths.js +40 -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/capability-template.js +118 -0
- package/dist/commands/new/capability-template.js.map +1 -0
- package/dist/commands/new/commit.js +62 -0
- package/dist/commands/new/commit.js.map +1 -0
- package/dist/commands/new/modifies.js +206 -0
- package/dist/commands/new/modifies.js.map +1 -0
- package/dist/commands/new/new.js +328 -0
- package/dist/commands/new/new.js.map +1 -0
- package/dist/commands/new/templates.js +279 -0
- package/dist/commands/new/templates.js.map +1 -0
- package/dist/commands/open.js +199 -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} +38 -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 +300 -0
- package/dist/commands/rebase/rebase.js.map +1 -0
- package/dist/commands/seed/commit.js +221 -0
- package/dist/commands/seed/commit.js.map +1 -0
- package/dist/commands/seed/plan.js +278 -0
- package/dist/commands/seed/plan.js.map +1 -0
- package/dist/commands/seed/seed.js +174 -0
- package/dist/commands/seed/seed.js.map +1 -0
- package/dist/commands/show/feature.js +182 -0
- package/dist/commands/show/feature.js.map +1 -0
- package/dist/commands/show/introduced.js +157 -0
- package/dist/commands/show/introduced.js.map +1 -0
- package/dist/commands/show/marks.js +53 -0
- package/dist/commands/show/marks.js.map +1 -0
- package/dist/commands/show/service.js +239 -0
- package/dist/commands/show/service.js.map +1 -0
- package/dist/commands/show/show.js +87 -0
- package/dist/commands/show/show.js.map +1 -0
- package/dist/commands/status/fleet-next.js +90 -0
- package/dist/commands/status/fleet-next.js.map +1 -0
- package/dist/commands/status/json.js +47 -0
- package/dist/commands/status/json.js.map +1 -0
- package/dist/commands/status/print.js +209 -0
- package/dist/commands/status/print.js.map +1 -0
- package/dist/commands/status/status.js +115 -0
- package/dist/commands/status/status.js.map +1 -0
- package/dist/commands/steps/steps.js +269 -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 +325 -0
- package/dist/commands/subsystem/move.js.map +1 -0
- package/dist/commands/subsystem/query.js +113 -0
- package/dist/commands/subsystem/query.js.map +1 -0
- package/dist/commands/subsystem/subsystem.js +98 -0
- package/dist/commands/subsystem/subsystem.js.map +1 -0
- package/dist/commands/subsystem/sync.js +318 -0
- package/dist/commands/subsystem/sync.js.map +1 -0
- package/dist/commands/subsystem/txn/exclude.js +132 -0
- package/dist/commands/subsystem/txn/exclude.js.map +1 -0
- package/dist/commands/subsystem/txn/links.js +128 -0
- package/dist/commands/subsystem/txn/links.js.map +1 -0
- package/dist/commands/subsystem/txn/report.js +174 -0
- package/dist/commands/subsystem/txn/report.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +196 -0
- package/dist/commands/subsystem/txn/txn.js.map +1 -0
- package/dist/commands/subsystem/txn/views.js +249 -0
- package/dist/commands/subsystem/txn/views.js.map +1 -0
- package/dist/commands/tasks/actions.js +107 -0
- package/dist/commands/tasks/actions.js.map +1 -0
- package/dist/commands/tasks/render.js +253 -0
- package/dist/commands/tasks/render.js.map +1 -0
- package/dist/commands/tasks/tasks.js +119 -0
- package/dist/commands/tasks/tasks.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +176 -0
- package/dist/commands/unarchive/manifest.js.map +1 -0
- package/dist/commands/unarchive/restore.js +254 -0
- package/dist/commands/unarchive/restore.js.map +1 -0
- package/dist/commands/unarchive/unarchive.js +59 -0
- package/dist/commands/unarchive/unarchive.js.map +1 -0
- package/dist/commands/validate/arch-coverage.js +245 -0
- package/dist/commands/validate/arch-coverage.js.map +1 -0
- package/dist/commands/validate/checks/fleet-shape.js +204 -0
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
- package/dist/commands/validate/checks/health-deps.js +58 -0
- package/dist/commands/validate/checks/health-deps.js.map +1 -0
- package/dist/commands/validate/checks/introduced.js +102 -0
- package/dist/commands/validate/checks/introduced.js.map +1 -0
- package/dist/commands/validate/checks/requirements.js +264 -0
- package/dist/commands/validate/checks/requirements.js.map +1 -0
- package/dist/commands/validate/checks/sources.js +85 -0
- package/dist/commands/validate/checks/sources.js.map +1 -0
- package/dist/commands/validate/coverage/covers-after.js +105 -0
- package/dist/commands/validate/coverage/covers-after.js.map +1 -0
- package/dist/commands/validate/feature.js +222 -0
- package/dist/commands/validate/feature.js.map +1 -0
- package/dist/commands/validate/fleet/census.js +93 -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 +371 -0
- package/dist/commands/validate/fleet/landscape.js.map +1 -0
- package/dist/commands/validate/fleet/load.js +118 -0
- package/dist/commands/validate/fleet/load.js.map +1 -0
- package/dist/commands/validate/fleet/map/attest.js +98 -0
- package/dist/commands/validate/fleet/map/attest.js.map +1 -0
- package/dist/commands/validate/fleet/map/bindings.js +107 -0
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
- package/dist/commands/validate/fleet/map/consumers.js +244 -0
- package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
- package/dist/commands/validate/fleet/map/isolation.js +116 -0
- package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
- package/dist/commands/validate/fleet/map/usecases.js +54 -0
- package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
- package/dist/commands/validate/fleet/obligations.js +266 -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 +71 -0
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js +162 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.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/views/fleet-project.js +217 -0
- package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
- package/dist/commands/validate/fleet/views/ids.js +75 -0
- package/dist/commands/validate/fleet/views/ids.js.map +1 -0
- package/dist/commands/validate/fleet/views/projects.js +278 -0
- package/dist/commands/validate/fleet/views/projects.js.map +1 -0
- package/dist/commands/validate/fleet/views/root-documents.js +83 -0
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +52 -0
- package/dist/commands/validate/fleet/views/stale.js.map +1 -0
- package/dist/commands/validate/links/corpus.js +89 -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/render/repeats.js +63 -0
- package/dist/commands/validate/render/repeats.js.map +1 -0
- package/dist/commands/validate/render/rollups.js +115 -0
- package/dist/commands/validate/render/rollups.js.map +1 -0
- package/dist/commands/validate/render/text.js +199 -0
- package/dist/commands/validate/render/text.js.map +1 -0
- package/dist/commands/validate/report.js +145 -0
- package/dist/commands/validate/report.js.map +1 -0
- package/dist/commands/validate/scope.js +113 -0
- package/dist/commands/validate/scope.js.map +1 -0
- package/dist/commands/validate/service/api.js +252 -0
- package/dist/commands/validate/service/api.js.map +1 -0
- package/dist/commands/validate/service/contracts/generated.js +134 -0
- package/dist/commands/validate/service/contracts/generated.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +241 -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 +177 -0
- package/dist/commands/validate/service/evidence-pins.js.map +1 -0
- package/dist/commands/validate/service/model/diverged.js +226 -0
- package/dist/commands/validate/service/model/diverged.js.map +1 -0
- package/dist/commands/validate/service/model/grade.js +163 -0
- package/dist/commands/validate/service/model/grade.js.map +1 -0
- package/dist/commands/validate/service/service.js +310 -0
- package/dist/commands/validate/service/service.js.map +1 -0
- package/dist/commands/validate/service/specs.js +279 -0
- package/dist/commands/validate/service/specs.js.map +1 -0
- package/dist/commands/validate/service/spine.js +148 -0
- package/dist/commands/validate/service/spine.js.map +1 -0
- package/dist/commands/validate/service/usecases/tags.js +113 -0
- package/dist/commands/validate/service/usecases/tags.js.map +1 -0
- package/dist/commands/validate/service/usecases/usecases.js +209 -0
- package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
- package/dist/commands/validate/validate.js +363 -0
- package/dist/commands/validate/validate.js.map +1 -0
- package/dist/commands/verify/cross/diff.js +178 -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 +81 -0
- package/dist/commands/verify/evidence/read.js.map +1 -0
- package/dist/commands/verify/frozen.js +263 -0
- package/dist/commands/verify/frozen.js.map +1 -0
- package/dist/commands/verify/record.js +278 -0
- package/dist/commands/verify/record.js.map +1 -0
- package/dist/commands/verify/report.js +206 -0
- package/dist/commands/verify/report.js.map +1 -0
- package/dist/commands/verify/results.js +328 -0
- package/dist/commands/verify/results.js.map +1 -0
- package/dist/commands/verify/verify.js +220 -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 +266 -0
- package/dist/commands/vouch/pack/print.js.map +1 -0
- package/dist/commands/vouch/pack/sections.js +141 -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 +230 -0
- package/dist/commands/vouch/run.js.map +1 -0
- package/dist/commands/vouch/sample/plan.js +111 -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 +155 -0
- package/dist/commands/vouch/vet/verify.js.map +1 -0
- package/dist/commands/vouch/vouch.js +169 -0
- package/dist/commands/vouch/vouch.js.map +1 -0
- package/dist/core/agent/agents-md/command-map.js +299 -0
- package/dist/core/agent/agents-md/command-map.js.map +1 -0
- package/dist/core/agent/agents-md/map/explain.js +46 -0
- package/dist/core/agent/agents-md/map/explain.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/context.js +50 -0
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js +59 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js +65 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
- package/dist/core/agent/agents-md/map/mcp.js +44 -0
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
- package/dist/core/agent/agents-md/map/status.js +78 -0
- package/dist/core/agent/agents-md/map/status.js.map +1 -0
- package/dist/core/agent/agents-md/map/subsystem.js +104 -0
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
- package/dist/core/agent/agents-md/map/usecases.js +138 -0
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
- package/dist/core/agent/agents-md/refusals.js +273 -0
- package/dist/core/agent/agents-md/refusals.js.map +1 -0
- package/dist/core/agent/agents-md.js +203 -0
- package/dist/core/agent/agents-md.js.map +1 -0
- package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +60 -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 +254 -0
- package/dist/core/agent/scaffold.js.map +1 -0
- package/dist/core/agent/support/report.js +144 -0
- package/dist/core/agent/support/report.js.map +1 -0
- package/dist/core/agent/tools/dialects.js +151 -0
- package/dist/core/agent/tools/dialects.js.map +1 -0
- package/dist/core/agent/tools/registry.js +193 -0
- package/dist/core/agent/tools/registry.js.map +1 -0
- package/dist/core/agent/workflows/adopt.js +280 -0
- package/dist/core/agent/workflows/adopt.js.map +1 -0
- package/dist/core/agent/workflows/check.js +373 -0
- package/dist/core/agent/workflows/check.js.map +1 -0
- package/dist/core/agent/workflows/closing.js +323 -0
- package/dist/core/agent/workflows/closing.js.map +1 -0
- package/dist/core/agent/workflows/feature.js +220 -0
- package/dist/core/agent/workflows/feature.js.map +1 -0
- package/dist/core/agent/workflows/implement.js +188 -0
- package/dist/core/agent/workflows/implement.js.map +1 -0
- package/dist/core/agent/workflows/reference/authoring.js +311 -0
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
- package/dist/core/agent/workflows/reference/codes.js +67 -0
- package/dist/core/agent/workflows/reference/codes.js.map +1 -0
- package/dist/core/agent/workflows/reference/done-check.js +210 -0
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
- package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
- package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
- package/dist/core/agent/workflows/reference/reference.js +19 -0
- package/dist/core/agent/workflows/reference/reference.js.map +1 -0
- package/dist/core/agent/workflows/reference/spine.js +338 -0
- package/dist/core/agent/workflows/reference/spine.js.map +1 -0
- package/dist/core/asyncapi/baseline/plan.js +43 -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 +146 -0
- package/dist/core/asyncapi/digest.js.map +1 -0
- package/dist/core/asyncapi/merge/components.js +92 -0
- package/dist/core/asyncapi/merge/components.js.map +1 -0
- package/dist/core/asyncapi/merge/error.js +38 -0
- package/dist/core/asyncapi/merge/error.js.map +1 -0
- package/dist/core/asyncapi/merge/markers.js +290 -0
- package/dist/core/asyncapi/merge/markers.js.map +1 -0
- package/dist/core/asyncapi/merge/merge.js +195 -0
- package/dist/core/asyncapi/merge/merge.js.map +1 -0
- package/dist/core/asyncapi/merge/pin.js +83 -0
- package/dist/core/asyncapi/merge/pin.js.map +1 -0
- package/dist/core/asyncapi/model.js +11 -0
- package/dist/core/asyncapi/model.js.map +1 -0
- package/dist/core/asyncapi/producers.js +50 -0
- package/dist/core/asyncapi/producers.js.map +1 -0
- package/dist/core/asyncapi/read.js +263 -0
- package/dist/core/asyncapi/read.js.map +1 -0
- package/dist/core/brief/brief.js +285 -0
- package/dist/core/brief/brief.js.map +1 -0
- package/dist/core/brief/checks.js +385 -0
- package/dist/core/brief/checks.js.map +1 -0
- package/dist/core/brief/landscape.js +273 -0
- package/dist/core/brief/landscape.js.map +1 -0
- package/dist/core/brief/map/extend.js +84 -0
- package/dist/core/brief/map/extend.js.map +1 -0
- package/dist/core/brief/map/owed.js +266 -0
- package/dist/core/brief/map/owed.js.map +1 -0
- package/dist/core/brief/targets.js +304 -0
- package/dist/core/brief/targets.js.map +1 -0
- package/dist/core/brief/unchecked.js +58 -0
- package/dist/core/brief/unchecked.js.map +1 -0
- package/dist/core/c4/arch.js +190 -0
- package/dist/core/c4/arch.js.map +1 -0
- package/dist/core/c4/direction/direction.js +114 -0
- package/dist/core/c4/direction/direction.js.map +1 -0
- package/dist/core/c4/likec4.js +243 -0
- package/dist/core/c4/likec4.js.map +1 -0
- package/dist/core/c4/locate/unlocated.js +150 -0
- package/dist/core/c4/locate/unlocated.js.map +1 -0
- package/dist/core/c4/parsed/deployment.js +160 -0
- package/dist/core/c4/parsed/deployment.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js +132 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
- package/dist/core/c4/parsed/specification.js +78 -0
- package/dist/core/c4/parsed/specification.js.map +1 -0
- package/dist/core/c4/parsed/values.js +110 -0
- package/dist/core/c4/parsed/values.js.map +1 -0
- package/dist/core/c4/parsed/view-ids.js +113 -0
- package/dist/core/c4/parsed/view-ids.js.map +1 -0
- package/dist/core/c4/prefetch/fleet.js +134 -0
- package/dist/core/c4/prefetch/fleet.js.map +1 -0
- package/dist/core/c4/project/architecture.js +161 -0
- package/dist/core/c4/project/architecture.js.map +1 -0
- package/dist/core/c4/project/batch.js +140 -0
- package/dist/core/c4/project/batch.js.map +1 -0
- package/dist/core/c4/project/documents.js +89 -0
- package/dist/core/c4/project/documents.js.map +1 -0
- package/dist/core/c4/project/load.js +219 -0
- package/dist/core/c4/project/load.js.map +1 -0
- package/dist/core/c4/project/staged.js +277 -0
- package/dist/core/c4/project/staged.js.map +1 -0
- package/dist/core/c4/resolve/attested.js +0 -0
- package/dist/core/c4/resolve/attested.js.map +1 -0
- package/dist/core/c4/resolve/external.js +51 -0
- package/dist/core/c4/resolve/external.js.map +1 -0
- package/dist/core/c4/resolve/service.js +174 -0
- package/dist/core/c4/resolve/service.js.map +1 -0
- package/dist/core/c4/resolve/steps.js +290 -0
- package/dist/core/c4/resolve/steps.js.map +1 -0
- package/dist/core/c4/resolve/systems.js +154 -0
- package/dist/core/c4/resolve/systems.js.map +1 -0
- package/dist/core/c4/root-project/exclude.js +352 -0
- package/dist/core/c4/root-project/exclude.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 +173 -0
- package/dist/core/c4/seed/template.js.map +1 -0
- package/dist/core/c4/service-model/created/models.js +96 -0
- package/dist/core/c4/service-model/created/models.js.map +1 -0
- package/dist/core/c4/service-model/documents.js +47 -0
- package/dist/core/c4/service-model/documents.js.map +1 -0
- package/dist/core/c4/service-model/fleet/extending.js +82 -0
- package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
- package/dist/core/c4/service-model/load.js +165 -0
- package/dist/core/c4/service-model/load.js.map +1 -0
- package/dist/core/c4/service-model/renderer.js +128 -0
- package/dist/core/c4/service-model/renderer.js.map +1 -0
- package/dist/core/c4/service-model/shape.js +106 -0
- package/dist/core/c4/service-model/shape.js.map +1 -0
- package/dist/core/c4/service-model/slice.js +188 -0
- package/dist/core/c4/service-model/slice.js.map +1 -0
- package/dist/core/c4/source-mask.js +369 -0
- package/dist/core/c4/source-mask.js.map +1 -0
- package/dist/core/c4/source-scan.js +308 -0
- package/dist/core/c4/source-scan.js.map +1 -0
- package/dist/core/c4/splice/authored-source.js +167 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -0
- package/dist/core/c4/splice/contract.js +30 -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/declared.js +87 -0
- package/dist/core/c4/splice/identity/declared.js.map +1 -0
- package/dist/core/c4/splice/identity/edges.js +73 -0
- package/dist/core/c4/splice/identity/edges.js.map +1 -0
- package/dist/core/c4/splice/identity/existing.js +80 -0
- package/dist/core/c4/splice/identity/existing.js.map +1 -0
- package/dist/core/c4/splice/identity/interior.js +80 -0
- package/dist/core/c4/splice/identity/interior.js.map +1 -0
- package/dist/core/c4/splice/identity/owner.js +50 -0
- package/dist/core/c4/splice/identity/owner.js.map +1 -0
- package/dist/core/c4/splice/landscape-merge.js +337 -0
- package/dist/core/c4/splice/landscape-merge.js.map +1 -0
- package/dist/core/c4/splice/model/merge.js +151 -0
- package/dist/core/c4/splice/model/merge.js.map +1 -0
- package/dist/core/c4/splice/placement.js +298 -0
- package/dist/core/c4/splice/placement.js.map +1 -0
- package/dist/core/c4/splice/text/spot.js +131 -0
- package/dist/core/c4/splice/text/spot.js.map +1 -0
- package/dist/core/c4/workspace.js +166 -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 +47 -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 +338 -0
- package/dist/core/capabilities/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/findings.js +174 -0
- package/dist/core/capabilities/realizes/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/join.js +123 -0
- package/dist/core/capabilities/realizes/join.js.map +1 -0
- package/dist/core/capabilities/rollup.js +205 -0
- package/dist/core/capabilities/rollup.js.map +1 -0
- package/dist/core/capabilities/tree.js +104 -0
- package/dist/core/capabilities/tree.js.map +1 -0
- package/dist/core/capabilities/usecase-join.js +267 -0
- package/dist/core/capabilities/usecase-join.js.map +1 -0
- package/dist/core/coherence/authoring/scaffold.js +185 -0
- package/dist/core/coherence/authoring/scaffold.js.map +1 -0
- package/dist/core/coherence/authoring/sentinels.js +75 -0
- package/dist/core/coherence/authoring/sentinels.js.map +1 -0
- package/dist/core/coherence/calls/called.js +52 -0
- package/dist/core/coherence/calls/called.js.map +1 -0
- package/dist/core/coherence/coherence.js +332 -0
- package/dist/core/coherence/coherence.js.map +1 -0
- package/dist/core/coherence/consumed/operations.js +70 -0
- package/dist/core/coherence/consumed/operations.js.map +1 -0
- package/dist/core/coherence/consumed/refusal.js +38 -0
- package/dist/core/coherence/consumed/refusal.js.map +1 -0
- package/dist/core/coherence/covers/health.js +85 -0
- package/dist/core/coherence/covers/health.js.map +1 -0
- package/dist/core/coherence/declared.js +295 -0
- package/dist/core/coherence/declared.js.map +1 -0
- package/dist/core/coherence/events/declared.js +191 -0
- package/dist/core/coherence/events/declared.js.map +1 -0
- package/dist/core/coherence/events/events.js +335 -0
- package/dist/core/coherence/events/events.js.map +1 -0
- package/dist/core/coherence/events/grades.js +189 -0
- package/dist/core/coherence/events/grades.js.map +1 -0
- package/dist/core/coherence/events/lookups.js +63 -0
- package/dist/core/coherence/events/lookups.js.map +1 -0
- package/dist/core/coherence/events/produced.js +100 -0
- package/dist/core/coherence/events/produced.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 +95 -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/coherence/retired/archived.js +285 -0
- package/dist/core/coherence/retired/archived.js.map +1 -0
- package/dist/core/coherence/retired/event-pair.js +62 -0
- package/dist/core/coherence/retired/event-pair.js.map +1 -0
- package/dist/core/coherence/retired/merged.js +79 -0
- package/dist/core/coherence/retired/merged.js.map +1 -0
- package/dist/core/coherence/retired/retirement.js +49 -0
- package/dist/core/coherence/retired/retirement.js.map +1 -0
- package/dist/core/coherence/retired/revival/arrivals.js +131 -0
- package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
- package/dist/core/coherence/retired/revival/revived.js +297 -0
- package/dist/core/coherence/retired/revival/revived.js.map +1 -0
- package/dist/core/conflict-markers.js +60 -0
- package/dist/core/conflict-markers.js.map +1 -0
- package/dist/core/consumers/after.js +113 -0
- package/dist/core/consumers/after.js.map +1 -0
- package/dist/core/consumers/consumers.js +296 -0
- package/dist/core/consumers/consumers.js.map +1 -0
- package/dist/core/consumers/current.js +211 -0
- package/dist/core/consumers/current.js.map +1 -0
- package/dist/core/consumers/scan.js +46 -0
- package/dist/core/consumers/scan.js.map +1 -0
- package/dist/core/consumers/state.js +27 -0
- package/dist/core/consumers/state.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 +341 -0
- package/dist/core/delta/delta.js.map +1 -0
- package/dist/core/delta/document.js +177 -0
- package/dist/core/delta/document.js.map +1 -0
- package/dist/core/delta/scope.js +48 -0
- package/dist/core/delta/scope.js.map +1 -0
- package/dist/core/delta/select.js +237 -0
- package/dist/core/delta/select.js.map +1 -0
- package/dist/core/dependencies/dependencies.js +141 -0
- package/dist/core/dependencies/dependencies.js.map +1 -0
- package/dist/core/dependencies/facts.js +165 -0
- package/dist/core/dependencies/facts.js.map +1 -0
- package/dist/core/dependencies/fanin.js +88 -0
- package/dist/core/dependencies/fanin.js.map +1 -0
- package/dist/core/dependencies/graph.js +106 -0
- package/dist/core/dependencies/graph.js.map +1 -0
- package/dist/core/deployment/delta.js +191 -0
- package/dist/core/deployment/delta.js.map +1 -0
- package/dist/core/deployment/objects.js +145 -0
- package/dist/core/deployment/objects.js.map +1 -0
- package/dist/core/diff/base-git.js +230 -0
- package/dist/core/diff/base-git.js.map +1 -0
- package/dist/core/diff/base-state.js +237 -0
- package/dist/core/diff/base-state.js.map +1 -0
- package/dist/core/diff/joins.js +136 -0
- package/dist/core/diff/joins.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +204 -0
- package/dist/core/diff/scope/changed-paths.js.map +1 -0
- package/dist/core/diff/scope/edges.js +82 -0
- package/dist/core/diff/scope/edges.js.map +1 -0
- package/dist/core/diff/semantic.js +312 -0
- package/dist/core/diff/semantic.js.map +1 -0
- package/dist/core/docs.js +37 -85
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +238 -0
- package/dist/core/doctor/agents.js.map +1 -0
- package/dist/core/doctor/config.js +139 -0
- package/dist/core/doctor/config.js.map +1 -0
- package/dist/core/doctor/doctor.js +355 -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/reports/scan.js +242 -0
- package/dist/core/doctor/reports/scan.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 +224 -0
- package/dist/core/document/apply.js.map +1 -0
- package/dist/core/{frontmatter.js → document/frontmatter.js} +103 -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 +286 -0
- package/dist/core/document/parse.js.map +1 -0
- package/dist/core/document/scenarios.js +238 -0
- package/dist/core/document/scenarios.js.map +1 -0
- package/dist/core/document/spec.js +234 -0
- package/dist/core/document/spec.js.map +1 -0
- package/dist/core/{config.js → envelope/config.js} +89 -18
- package/dist/core/envelope/config.js.map +1 -0
- package/dist/core/envelope/json.js +209 -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 +180 -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 +194 -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/documents.js +227 -0
- package/dist/core/explain/terms/documents.js.map +1 -0
- package/dist/core/explain/terms/done-check.js +144 -0
- package/dist/core/explain/terms/done-check.js.map +1 -0
- package/dist/core/explain/terms/entry.js +14 -0
- package/dist/core/explain/terms/entry.js.map +1 -0
- package/dist/core/explain/terms/stages.js +71 -0
- package/dist/core/explain/terms/stages.js.map +1 -0
- package/dist/core/explain/terms/tasks.js +97 -0
- package/dist/core/explain/terms/tasks.js.map +1 -0
- package/dist/core/explain/terms.js +103 -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/explore.js +204 -0
- package/dist/core/explore/explore.js.map +1 -0
- package/dist/core/fleet-context.js +174 -146
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +208 -0
- package/dist/core/gate/checks.js.map +1 -0
- package/dist/core/gate/partners.js +246 -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 +176 -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 +210 -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 +109 -0
- package/dist/core/glossary/delta.js.map +1 -0
- package/dist/core/glossary/tree.js +73 -0
- package/dist/core/glossary/tree.js.map +1 -0
- package/dist/core/{concurrency.js → kernel/concurrency.js} +19 -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/fqn/ancestors.js +65 -0
- package/dist/core/kernel/ids/fqn/ancestors.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} +56 -4
- package/dist/core/kernel/path-safety.js.map +1 -0
- package/dist/core/{records.js → kernel/records.js} +11 -0
- package/dist/core/kernel/records.js.map +1 -0
- package/dist/core/links/backlinks.js +66 -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 +118 -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 +265 -0
- package/dist/core/links/parse.js.map +1 -0
- package/dist/core/mcp/argv.js +212 -0
- package/dist/core/mcp/argv.js.map +1 -0
- package/dist/core/mcp/author-tools.js +73 -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 +324 -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 +207 -0
- package/dist/core/openapi/baseline/gate.js.map +1 -0
- package/dist/core/openapi/baseline/plan.js +142 -0
- package/dist/core/openapi/baseline/plan.js.map +1 -0
- package/dist/core/openapi/baseline/record.js +174 -0
- package/dist/core/openapi/baseline/record.js.map +1 -0
- package/dist/core/openapi/contract-text.js +33 -0
- package/dist/core/openapi/contract-text.js.map +1 -0
- package/dist/core/openapi/contract-tree.js +301 -0
- package/dist/core/openapi/contract-tree.js.map +1 -0
- package/dist/core/openapi/depth.js +136 -0
- package/dist/core/openapi/depth.js.map +1 -0
- package/dist/core/openapi/digest.js +239 -0
- package/dist/core/openapi/digest.js.map +1 -0
- package/dist/core/{openapi.js → openapi/doc.js} +54 -108
- package/dist/core/openapi/doc.js.map +1 -0
- package/dist/core/openapi/merge/components.js +199 -0
- package/dist/core/openapi/merge/components.js.map +1 -0
- package/dist/core/openapi/merge/error.js +16 -0
- package/dist/core/openapi/merge/error.js.map +1 -0
- package/dist/core/openapi/merge/markers.js +204 -0
- package/dist/core/openapi/merge/markers.js.map +1 -0
- package/dist/core/openapi/merge/merge.js +232 -0
- package/dist/core/openapi/merge/merge.js.map +1 -0
- package/dist/core/openapi/merge/pin.js +110 -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 +220 -0
- package/dist/core/openspec/inventory.js.map +1 -0
- package/dist/core/openspec/model/mapping.js +78 -0
- package/dist/core/openspec/model/mapping.js.map +1 -0
- package/dist/core/openspec/model/model.js +70 -0
- package/dist/core/openspec/model/model.js.map +1 -0
- package/dist/core/openspec/scan/changes.js +241 -0
- package/dist/core/openspec/scan/changes.js.map +1 -0
- package/dist/core/openspec/scan/config.js +191 -0
- package/dist/core/openspec/scan/config.js.map +1 -0
- package/dist/core/openspec/scan/shape.js +304 -0
- package/dist/core/openspec/scan/shape.js.map +1 -0
- package/dist/core/openspec/scan/walk.js +116 -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 +157 -0
- package/dist/core/pack/living.js.map +1 -0
- package/dist/core/pack/pack.js +120 -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/projection/introduced.js +14 -0
- package/dist/core/projection/introduced.js.map +1 -0
- package/dist/core/provenance/findings.js +244 -0
- package/dist/core/provenance/findings.js.map +1 -0
- package/dist/core/provenance/git.js +201 -0
- package/dist/core/provenance/git.js.map +1 -0
- package/dist/core/provenance/gitq/moves.js +139 -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 +280 -0
- package/dist/core/provenance/sources.js.map +1 -0
- package/dist/core/provenance/stamp.js +220 -0
- package/dist/core/provenance/stamp.js.map +1 -0
- package/dist/core/provenance/walk.js +157 -0
- package/dist/core/provenance/walk.js.map +1 -0
- package/dist/core/repo/authored/paths.js +210 -0
- package/dist/core/repo/authored/paths.js.map +1 -0
- package/dist/core/repo/entries.js +126 -0
- package/dist/core/repo/entries.js.map +1 -0
- package/dist/core/repo/paths.js +322 -0
- package/dist/core/repo/paths.js.map +1 -0
- package/dist/core/repo/repo.js +308 -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 +61 -0
- package/dist/core/repo/tree/find.js.map +1 -0
- package/dist/core/repo/tree/fs.js +90 -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/render/body.js +258 -0
- package/dist/core/repo/tree/render/body.js.map +1 -0
- package/dist/core/repo/tree/render/projects.js +182 -0
- package/dist/core/repo/tree/render/projects.js.map +1 -0
- package/dist/core/repo/tree/render/service/header.js +125 -0
- package/dist/core/repo/tree/render/service/header.js.map +1 -0
- package/dist/core/repo/tree/render/service/peers.js +28 -0
- package/dist/core/repo/tree/render/service/peers.js.map +1 -0
- package/dist/core/repo/tree/render/service/view-id.js +67 -0
- package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
- package/dist/core/repo/tree/render/service/views.js +78 -0
- package/dist/core/repo/tree/render/service/views.js.map +1 -0
- package/dist/core/repo/tree/render/stale.js +42 -0
- package/dist/core/repo/tree/render/stale.js.map +1 -0
- package/dist/core/repo/tree/render/text.js +84 -0
- package/dist/core/repo/tree/render/text.js.map +1 -0
- package/dist/core/repo/tree/render/views.js +293 -0
- package/dist/core/repo/tree/render/views.js.map +1 -0
- package/dist/core/repo/tree/walk.js +255 -0
- package/dist/core/repo/tree/walk.js.map +1 -0
- package/dist/core/results.js +146 -14
- package/dist/core/results.js.map +1 -1
- package/dist/core/scaffold/landscape.js +239 -0
- package/dist/core/scaffold/landscape.js.map +1 -0
- package/dist/core/scaffold/legacy.js +271 -0
- package/dist/core/scaffold/legacy.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 +252 -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 +268 -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 +95 -0
- package/dist/core/staging/recovery/intent.js.map +1 -0
- package/dist/core/staging/recovery/recover.js +204 -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/manifest.js +188 -0
- package/dist/core/staging/snapshot/manifest.js.map +1 -0
- package/dist/core/staging/snapshot/snapshot.js +196 -0
- package/dist/core/staging/snapshot/snapshot.js.map +1 -0
- package/dist/core/staging/txn/forward.js +181 -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 +60 -0
- package/dist/core/status/actions/execution.js.map +1 -0
- package/dist/core/status/contracts.js +126 -0
- package/dist/core/status/contracts.js.map +1 -0
- package/dist/core/status/feature/artifacts.js +196 -0
- package/dist/core/status/feature/artifacts.js.map +1 -0
- package/dist/core/status/feature/feature.js +214 -0
- package/dist/core/status/feature/feature.js.map +1 -0
- package/dist/core/status/feature/next.js +163 -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/feature/words.js +96 -0
- package/dist/core/status/feature/words.js.map +1 -0
- package/dist/core/status/fleet/fleet.js +216 -0
- package/dist/core/status/fleet/fleet.js.map +1 -0
- package/dist/core/status/fleet/next.js +302 -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 +216 -0
- package/dist/core/status/verification.js.map +1 -0
- package/dist/core/tasks/packet.js +199 -0
- package/dist/core/tasks/packet.js.map +1 -0
- package/dist/core/tasks/record.js +43 -0
- package/dist/core/tasks/record.js.map +1 -0
- package/dist/core/tasks/sources.js +181 -0
- package/dist/core/tasks/sources.js.map +1 -0
- package/dist/core/tasks/stamps.js +76 -0
- package/dist/core/tasks/stamps.js.map +1 -0
- package/dist/core/tasks/unit.js +0 -0
- package/dist/core/tasks/unit.js.map +1 -0
- package/dist/core/usecases/capability.js +166 -0
- package/dist/core/usecases/capability.js.map +1 -0
- package/dist/core/usecases/delta/claims.js +140 -0
- package/dist/core/usecases/delta/claims.js.map +1 -0
- package/dist/core/usecases/delta/flows.js +101 -0
- package/dist/core/usecases/delta/flows.js.map +1 -0
- package/dist/core/usecases/delta/modified.js +315 -0
- package/dist/core/usecases/delta/modified.js.map +1 -0
- package/dist/core/usecases/delta/overlay.js +76 -0
- package/dist/core/usecases/delta/overlay.js.map +1 -0
- package/dist/core/usecases/fleet.js +296 -0
- package/dist/core/usecases/fleet.js.map +1 -0
- package/dist/core/usecases/grade/capability-tag.js +93 -0
- package/dist/core/usecases/grade/capability-tag.js.map +1 -0
- package/dist/core/usecases/grade/feature.js +88 -0
- package/dist/core/usecases/grade/feature.js.map +1 -0
- package/dist/core/usecases/grade/requirement-tag.js +94 -0
- package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
- package/dist/core/usecases/grade/steps.js +266 -0
- package/dist/core/usecases/grade/steps.js.map +1 -0
- package/dist/core/usecases/grade/usecases.js +112 -0
- package/dist/core/usecases/grade/usecases.js.map +1 -0
- package/dist/core/usecases/operations.js +123 -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/service/exclude.js +119 -0
- package/dist/core/usecases/service/exclude.js.map +1 -0
- package/dist/core/usecases/service/flows.js +164 -0
- package/dist/core/usecases/service/flows.js.map +1 -0
- package/dist/core/usecases/suites/participants.js +83 -0
- package/dist/core/usecases/suites/participants.js.map +1 -0
- package/dist/core/usecases/suites/suites.js +45 -0
- package/dist/core/usecases/suites/suites.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 +180 -0
- package/dist/core/verify/answers.js.map +1 -0
- package/dist/core/verify/build.js +170 -0
- package/dist/core/verify/build.js.map +1 -0
- package/dist/core/verify/checklist.js +259 -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/location.js +67 -0
- package/dist/core/verify/location.js.map +1 -0
- package/dist/core/verify/pins/lint.js +158 -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/read/file.js +224 -0
- package/dist/core/verify/read/file.js.map +1 -0
- package/dist/core/verify/read/shape.js +169 -0
- package/dist/core/verify/read/shape.js.map +1 -0
- package/dist/core/verify/read/union.js +105 -0
- package/dist/core/verify/read/union.js.map +1 -0
- package/dist/core/verify/record.js +165 -0
- package/dist/core/verify/record.js.map +1 -0
- package/dist/core/verify/store/commit.js +99 -0
- package/dist/core/verify/store/commit.js.map +1 -0
- package/dist/core/verify/store/render.js +61 -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 +59 -0
- package/dist/core/vocabulary/issue.js.map +1 -0
- package/dist/core/vocabulary/maturity.js +216 -0
- package/dist/core/vocabulary/maturity.js.map +1 -0
- package/dist/core/vocabulary/report.js +90 -0
- 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 +232 -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 +336 -0
- package/examples/docs/architecture/obligations.yaml +23 -0
- package/examples/docs/architecture/permissions.yaml +50 -0
- package/examples/docs/architecture/services.likec4 +70 -0
- package/examples/docs/architecture/subsystems.likec4 +18 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +74 -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 +132 -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 +31 -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 +73 -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 +8 -0
- package/examples/docs/loam.json +3 -0
- package/examples/docs/services/checkout-web/model.likec4 +92 -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 +134 -0
- package/examples/docs/services/order-service/asyncapi.yaml +109 -0
- package/examples/docs/services/order-service/health.yaml +57 -0
- package/examples/docs/services/order-service/model.likec4 +126 -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 +76 -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 +68 -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 +120 -0
- package/examples/docs/services/platform/identity-service/health.yaml +70 -0
- package/examples/docs/services/platform/identity-service/model.likec4 +81 -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 +200 -0
- package/examples/docs/services/platform/notification-service/health.yaml +40 -0
- package/examples/docs/services/platform/notification-service/model.likec4 +97 -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 +0 -320
- 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 +0 -317
- 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 +0 -253
- 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 +0 -225
- 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 +0 -47
- 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
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything that happens to a `TargetReport` around the checks themselves:
|
|
3
|
+
* how one is built when a target could not be read at all, how a finding's
|
|
4
|
+
* evidence is capped, and how the set is summed. How it PRINTS is
|
|
5
|
+
* `./render/text.ts`.
|
|
6
|
+
*
|
|
7
|
+
* A module rather than the tail of validate.ts because the rollup and the
|
|
8
|
+
* renderer have to agree. The `--all` footer and the `--json` summary are the
|
|
9
|
+
* same `summary()` — computed here once and HANDED to the renderer, which
|
|
10
|
+
* never counts for itself — and the cap reaches both payloads from one place;
|
|
11
|
+
* a second copy of either is how a footer starts printing a number its own
|
|
12
|
+
* findings contradict.
|
|
13
|
+
*/
|
|
14
|
+
import { isAbsolute } from "node:path";
|
|
15
|
+
import { DocsRepoUnavailableError } from "../../core/repo/state.js";
|
|
16
|
+
import { repoPath } from "../../core/envelope/json.js";
|
|
17
|
+
import { countSeverity, subjectsWith, } from "../../core/vocabulary/report.js";
|
|
18
|
+
export function summary(targets) {
|
|
19
|
+
return {
|
|
20
|
+
services: targets.filter((t) => t.kind === "service").length,
|
|
21
|
+
features: targets.filter((t) => t.kind === "feature").length,
|
|
22
|
+
errors: countSeverity(targets, "error"),
|
|
23
|
+
warnings: countSeverity(targets, "warn"),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* How many services this run could not check, for the rollup line and the
|
|
28
|
+
* `--json` payload.
|
|
29
|
+
*
|
|
30
|
+
* Counted off the findings rather than alongside them, so the rollup line
|
|
31
|
+
* and the per-service findings can never disagree about how many services
|
|
32
|
+
* this run could not check. The by-SUBJECT rule (a service whose spec.md and
|
|
33
|
+
* arch.spec.md both name `sources` raises two findings and is one service) is
|
|
34
|
+
* `subjectsWith`'s, spelled once for every findings-derived rollup — the
|
|
35
|
+
* fleet scorecard counts through the same function on the same code, so its
|
|
36
|
+
* number and this one are structurally the same number. The code is a LITERAL,
|
|
37
|
+
* as it is at the emission site (checks/sources.ts), rather than a shared
|
|
38
|
+
* constant, because the stable-code collector reads counting sites too, and it
|
|
39
|
+
* refuses a slot it cannot read — the collector is also what convicts a typo
|
|
40
|
+
* here.
|
|
41
|
+
*/
|
|
42
|
+
export function unverifiableSubjects(targets) {
|
|
43
|
+
return subjectsWith(targets, "sources.unverifiable-from-here");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* How many `details` lines any one finding may print before the rest are
|
|
47
|
+
* summarised away.
|
|
48
|
+
*
|
|
49
|
+
* A finding's details are evidence, not a log: LikeC4 reports one syntax error
|
|
50
|
+
* as dozens of cascading diagnostics, and a fleet-sized repo multiplies that by
|
|
51
|
+
* every target that mentions the file. The report is read by a person scrolling
|
|
52
|
+
* a CI log and by an agent with a context window, and neither of them is helped
|
|
53
|
+
* by the four-hundredth copy. The cap is applied to the JSON payload too, on
|
|
54
|
+
* purpose: `--json` is the interface an agent pipes, and an unbounded array is
|
|
55
|
+
* the same denial-of-attention there, just machine-readable.
|
|
56
|
+
*/
|
|
57
|
+
const DETAIL_LIMIT = 10;
|
|
58
|
+
/** Truncate every finding's details, marking what was dropped so nothing looks complete when it is not. */
|
|
59
|
+
export function capDetails(t) {
|
|
60
|
+
return {
|
|
61
|
+
...t,
|
|
62
|
+
findings: t.findings.map((f) => {
|
|
63
|
+
const details = f.details ?? [];
|
|
64
|
+
if (details.length <= DETAIL_LIMIT)
|
|
65
|
+
return f;
|
|
66
|
+
return {
|
|
67
|
+
...f,
|
|
68
|
+
details: [...details.slice(0, DETAIL_LIMIT), `… (+${details.length - DETAIL_LIMIT} more)`],
|
|
69
|
+
};
|
|
70
|
+
}),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* One target's checks, with an IO exception turned into a finding ON that
|
|
75
|
+
* target instead of aborting the run.
|
|
76
|
+
*
|
|
77
|
+
* A fleet gate that dies on the first unreadable file reports nothing about the
|
|
78
|
+
* other ninety-nine services — one bad permission bit, one file that is a
|
|
79
|
+
* dangling symlink, and CI's answer to "how is the fleet" becomes a stack
|
|
80
|
+
* trace. The failure is real and it is an error, so the run still exits 1; what
|
|
81
|
+
* changes is that everything else is still graded, and the finding names the
|
|
82
|
+
* service and the path instead of arriving as the `internal` catch-all.
|
|
83
|
+
* `docsDir` is how it names the path: see `shownPath`.
|
|
84
|
+
*/
|
|
85
|
+
export async function guarded(target, docsDir, run) {
|
|
86
|
+
try {
|
|
87
|
+
return await run();
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
// A docs repo that vanished mid-run is not one target's problem — it is the
|
|
91
|
+
// whole run's, and the action's own catch reports it.
|
|
92
|
+
if (err instanceof DocsRepoUnavailableError)
|
|
93
|
+
throw err;
|
|
94
|
+
const abs = err.path;
|
|
95
|
+
const path = abs === undefined ? undefined : shownPath(docsDir, abs);
|
|
96
|
+
// Node spells the same absolute path inside its own message
|
|
97
|
+
// (`EACCES: permission denied, open '…'`), so the reason is respelled
|
|
98
|
+
// with it — otherwise the one finding would name the file twice, two ways.
|
|
99
|
+
const said = err instanceof Error ? err.message : String(err);
|
|
100
|
+
const reason = abs === undefined || path === undefined ? said : said.replaceAll(abs, path);
|
|
101
|
+
return {
|
|
102
|
+
kind: target.kind,
|
|
103
|
+
id: target.id,
|
|
104
|
+
findings: [
|
|
105
|
+
{
|
|
106
|
+
severity: "error",
|
|
107
|
+
code: target.kind === "service" ? "service.unreadable" : "feature.unreadable",
|
|
108
|
+
subject: target.id,
|
|
109
|
+
message: `${target.id}: ${path === undefined ? "an artifact" : path} could not be read — ` +
|
|
110
|
+
`nothing about this ${target.kind} was checked. ${reason}`,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A path as a finding's message names it: repo-relative and `/`-separated when
|
|
118
|
+
* it is inside the docs repo, so the report reads the same on every machine
|
|
119
|
+
* and in every CI log — an absolute path names the checkout that ran the
|
|
120
|
+
* check, not the file in the repository. A path OUTSIDE the docs repo (a
|
|
121
|
+
* source file in the service repo loam is standing in) keeps its absolute
|
|
122
|
+
* spelling: the `../` route to it depends on where two checkouts happen to
|
|
123
|
+
* sit, which is exactly what a relative path was meant to stop mattering.
|
|
124
|
+
*/
|
|
125
|
+
function shownPath(docsDir, abs) {
|
|
126
|
+
const rel = repoPath(docsDir, abs);
|
|
127
|
+
return rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute(rel) ? abs : rel;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* `target.ambiguous` — the positional named a service AND a feature. The tie is
|
|
131
|
+
* still broken the way it always was (the feature wins), because changing which
|
|
132
|
+
* one is picked would silently re-target every script that relies on it; what
|
|
133
|
+
* is new is that the run says which reading it took and how to force the other.
|
|
134
|
+
*/
|
|
135
|
+
export function ambiguousTarget(arg, chosen) {
|
|
136
|
+
const other = chosen === "feature" ? "service" : "feature";
|
|
137
|
+
return {
|
|
138
|
+
severity: "warn",
|
|
139
|
+
code: "target.ambiguous",
|
|
140
|
+
subject: arg,
|
|
141
|
+
message: `'${arg}' names both a service and a feature — validated as the ${chosen}. ` +
|
|
142
|
+
`Pass --${other} ${arg} for the other reading.`,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/commands/validate/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AACvD,OAAO,EACL,aAAa,EACb,YAAY,GAGb,MAAM,iCAAiC,CAAC;AAiBzC,MAAM,UAAU,OAAO,CAAC,OAAuB;IAC7C,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,MAAM;QAC5D,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,MAAM;QAC5D,MAAM,EAAE,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC;QACvC,QAAQ,EAAE,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC;KACzC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAuB;IAC1D,OAAO,YAAY,CAAC,OAAO,EAAE,gCAAgC,CAAC,CAAC;AACjE,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB,2GAA2G;AAC3G,MAAM,UAAU,UAAU,CAAC,CAAe;IACxC,OAAO;QACL,GAAG,CAAC;QACJ,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YAC7B,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC;YAChC,IAAI,OAAO,CAAC,MAAM,IAAI,YAAY;gBAAE,OAAO,CAAC,CAAC;YAC7C,OAAO;gBACL,GAAG,CAAC;gBACJ,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,OAAO,OAAO,CAAC,MAAM,GAAG,YAAY,QAAQ,CAAC;aAC3F,CAAC;QACJ,CAAC,CAAC;KACH,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,MAAmD,EACnD,OAAe,EACf,GAAgC;IAEhC,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,EAAE,CAAC;IACrB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,4EAA4E;QAC5E,sDAAsD;QACtD,IAAI,GAAG,YAAY,wBAAwB;YAAE,MAAM,GAAG,CAAC;QACvD,MAAM,GAAG,GAAI,GAA6B,CAAC,IAAI,CAAC;QAChD,MAAM,IAAI,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACrE,4DAA4D;QAC5D,sEAAsE;QACtE,2EAA2E;QAC3E,MAAM,IAAI,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,GAAG,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC3F,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,EAAE,EAAE,MAAM,CAAC,EAAE;YACb,QAAQ,EAAE;gBACR;oBACE,QAAQ,EAAE,OAAO;oBACjB,IAAI,EAAE,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,oBAAoB;oBAC7E,OAAO,EAAE,MAAM,CAAC,EAAE;oBAClB,OAAO,EACL,GAAG,MAAM,CAAC,EAAE,KAAK,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,uBAAuB;wBACjF,sBAAsB,MAAM,CAAC,IAAI,iBAAiB,MAAM,EAAE;iBAC7D;aACF;SACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,SAAS,CAAC,OAAe,EAAE,GAAW;IAC7C,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACnC,OAAO,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AAC5F,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,MAA6B;IACxE,MAAM,KAAK,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,OAAO;QACL,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,kBAAkB;QACxB,OAAO,EAAE,GAAG;QACZ,OAAO,EACL,IAAI,GAAG,2DAA2D,MAAM,IAAI;YAC5E,UAAU,KAAK,IAAI,GAAG,yBAAyB;KAClD,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Everything that happens to a `TargetReport` around the checks themselves:\n * how one is built when a target could not be read at all, how a finding's\n * evidence is capped, and how the set is summed. How it PRINTS is\n * `./render/text.ts`.\n *\n * A module rather than the tail of validate.ts because the rollup and the\n * renderer have to agree. The `--all` footer and the `--json` summary are the\n * same `summary()` — computed here once and HANDED to the renderer, which\n * never counts for itself — and the cap reaches both payloads from one place;\n * a second copy of either is how a footer starts printing a number its own\n * findings contradict.\n */\nimport { isAbsolute } from \"node:path\";\nimport { DocsRepoUnavailableError } from \"../../core/repo/state.js\";\nimport { repoPath } from \"../../core/envelope/json.js\";\nimport {\n countSeverity,\n subjectsWith,\n type Finding,\n type TargetReport,\n} from \"../../core/vocabulary/report.js\";\n\n/**\n * The rollup `--json` carries and the `--all` footer prints.\n *\n * Four named fields rather than a `Record<string, number>`: the keys ARE the\n * contract, and under an index signature every read of them was an assertion\n * (`s.errors!`) that tsc could not check — rename one and the compiler stayed\n * quiet while the footer printed `undefined errors`.\n */\nexport interface ValidateSummary {\n services: number;\n features: number;\n errors: number;\n warnings: number;\n}\n\nexport function summary(targets: TargetReport[]): ValidateSummary {\n return {\n services: targets.filter((t) => t.kind === \"service\").length,\n features: targets.filter((t) => t.kind === \"feature\").length,\n errors: countSeverity(targets, \"error\"),\n warnings: countSeverity(targets, \"warn\"),\n };\n}\n\n/**\n * How many services this run could not check, for the rollup line and the\n * `--json` payload.\n *\n * Counted off the findings rather than alongside them, so the rollup line\n * and the per-service findings can never disagree about how many services\n * this run could not check. The by-SUBJECT rule (a service whose spec.md and\n * arch.spec.md both name `sources` raises two findings and is one service) is\n * `subjectsWith`'s, spelled once for every findings-derived rollup — the\n * fleet scorecard counts through the same function on the same code, so its\n * number and this one are structurally the same number. The code is a LITERAL,\n * as it is at the emission site (checks/sources.ts), rather than a shared\n * constant, because the stable-code collector reads counting sites too, and it\n * refuses a slot it cannot read — the collector is also what convicts a typo\n * here.\n */\nexport function unverifiableSubjects(targets: TargetReport[]): number {\n return subjectsWith(targets, \"sources.unverifiable-from-here\");\n}\n\n/**\n * How many `details` lines any one finding may print before the rest are\n * summarised away.\n *\n * A finding's details are evidence, not a log: LikeC4 reports one syntax error\n * as dozens of cascading diagnostics, and a fleet-sized repo multiplies that by\n * every target that mentions the file. The report is read by a person scrolling\n * a CI log and by an agent with a context window, and neither of them is helped\n * by the four-hundredth copy. The cap is applied to the JSON payload too, on\n * purpose: `--json` is the interface an agent pipes, and an unbounded array is\n * the same denial-of-attention there, just machine-readable.\n */\nconst DETAIL_LIMIT = 10;\n\n/** Truncate every finding's details, marking what was dropped so nothing looks complete when it is not. */\nexport function capDetails(t: TargetReport): TargetReport {\n return {\n ...t,\n findings: t.findings.map((f) => {\n const details = f.details ?? [];\n if (details.length <= DETAIL_LIMIT) return f;\n return {\n ...f,\n details: [...details.slice(0, DETAIL_LIMIT), `… (+${details.length - DETAIL_LIMIT} more)`],\n };\n }),\n };\n}\n\n/**\n * One target's checks, with an IO exception turned into a finding ON that\n * target instead of aborting the run.\n *\n * A fleet gate that dies on the first unreadable file reports nothing about the\n * other ninety-nine services — one bad permission bit, one file that is a\n * dangling symlink, and CI's answer to \"how is the fleet\" becomes a stack\n * trace. The failure is real and it is an error, so the run still exits 1; what\n * changes is that everything else is still graded, and the finding names the\n * service and the path instead of arriving as the `internal` catch-all.\n * `docsDir` is how it names the path: see `shownPath`.\n */\nexport async function guarded(\n target: { kind: \"service\" | \"feature\"; id: string },\n docsDir: string,\n run: () => Promise<TargetReport>,\n): Promise<TargetReport> {\n try {\n return await run();\n } catch (err) {\n // A docs repo that vanished mid-run is not one target's problem — it is the\n // whole run's, and the action's own catch reports it.\n if (err instanceof DocsRepoUnavailableError) throw err;\n const abs = (err as NodeJS.ErrnoException).path;\n const path = abs === undefined ? undefined : shownPath(docsDir, abs);\n // Node spells the same absolute path inside its own message\n // (`EACCES: permission denied, open '…'`), so the reason is respelled\n // with it — otherwise the one finding would name the file twice, two ways.\n const said = err instanceof Error ? err.message : String(err);\n const reason = abs === undefined || path === undefined ? said : said.replaceAll(abs, path);\n return {\n kind: target.kind,\n id: target.id,\n findings: [\n {\n severity: \"error\",\n code: target.kind === \"service\" ? \"service.unreadable\" : \"feature.unreadable\",\n subject: target.id,\n message:\n `${target.id}: ${path === undefined ? \"an artifact\" : path} could not be read — ` +\n `nothing about this ${target.kind} was checked. ${reason}`,\n },\n ],\n };\n }\n}\n\n/**\n * A path as a finding's message names it: repo-relative and `/`-separated when\n * it is inside the docs repo, so the report reads the same on every machine\n * and in every CI log — an absolute path names the checkout that ran the\n * check, not the file in the repository. A path OUTSIDE the docs repo (a\n * source file in the service repo loam is standing in) keeps its absolute\n * spelling: the `../` route to it depends on where two checkouts happen to\n * sit, which is exactly what a relative path was meant to stop mattering.\n */\nfunction shownPath(docsDir: string, abs: string): string {\n const rel = repoPath(docsDir, abs);\n return rel === \"\" || rel === \"..\" || rel.startsWith(\"../\") || isAbsolute(rel) ? abs : rel;\n}\n\n/**\n * `target.ambiguous` — the positional named a service AND a feature. The tie is\n * still broken the way it always was (the feature wins), because changing which\n * one is picked would silently re-target every script that relies on it; what\n * is new is that the run says which reading it took and how to force the other.\n */\nexport function ambiguousTarget(arg: string, chosen: \"service\" | \"feature\"): Finding {\n const other = chosen === \"feature\" ? \"service\" : \"feature\";\n return {\n severity: \"warn\",\n code: \"target.ambiguous\",\n subject: arg,\n message:\n `'${arg}' names both a service and a feature — validated as the ${chosen}. ` +\n `Pass --${other} ${arg} for the other reading.`,\n };\n}\n"]}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a `--base` run does to the landscape target, which is the one target
|
|
3
|
+
* whose subject is not a directory.
|
|
4
|
+
*
|
|
5
|
+
* Every other target is graded whole or not at all: a service or a feature the
|
|
6
|
+
* branch touched is graded by exactly the checks `--all` runs on it. The
|
|
7
|
+
* landscape cannot be, for two reasons that pull in opposite directions, and
|
|
8
|
+
* both are defects a docs-repo PR gate actually shipped with.
|
|
9
|
+
*
|
|
10
|
+
* IT GRADED TOO MUCH. The landscape target carries the fleet-wide
|
|
11
|
+
* vocabularies' warnings — a permission nobody requires, a capability nobody
|
|
12
|
+
* realizes, an obligation nobody applies, a glossary term nobody cites — and
|
|
13
|
+
* the AGENTS.md stamp. Every one of those grades a file other than the map, so
|
|
14
|
+
* any `architecture/` branch re-graded all of them, and under `--strict` a
|
|
15
|
+
* one-line map edit on the adopted example fleet went red on four warnings that
|
|
16
|
+
* were all there at the base. Those findings now ride a scoped run only when the
|
|
17
|
+
* file that carries them changed.
|
|
18
|
+
*
|
|
19
|
+
* IT GRADED TOO LITTLE. `spine.op-undefined` is graded on the PROVIDER's
|
|
20
|
+
* service target, so an edge the branch drew or re-spelled on the map was
|
|
21
|
+
* graded by no scoped run unless the provider happened to change too —
|
|
22
|
+
* README's own `authorisePayment` example merged green through both PR gates.
|
|
23
|
+
* Those edges are now graded here, against their providers' contracts, and
|
|
24
|
+
* ONLY those edges: pulling the provider into scope instead would grade every
|
|
25
|
+
* document it already has, which on a spine-first fleet means
|
|
26
|
+
* `service.no-model` reddening exactly the labour that path asks for.
|
|
27
|
+
*/
|
|
28
|
+
import { existsSync } from "node:fs";
|
|
29
|
+
import { serviceResolver } from "../../core/c4/resolve/service.js";
|
|
30
|
+
import { edgeKey } from "../../core/diff/scope/edges.js";
|
|
31
|
+
import { touched } from "../../core/diff/scope/changed-paths.js";
|
|
32
|
+
import { repoPath } from "../../core/envelope/json.js";
|
|
33
|
+
import { inOrder } from "../../core/kernel/concurrency.js";
|
|
34
|
+
import { serviceTreePath } from "../../core/kernel/ids/dirs.js";
|
|
35
|
+
import { readOpenapi } from "../../core/openapi/doc.js";
|
|
36
|
+
import { capabilityDocsDir, glossaryDir } from "../../core/repo/authored/paths.js";
|
|
37
|
+
import { agentsPath, capabilitiesPath, obligationsPath, permissionsPath, servicePathsAt } from "../../core/repo/paths.js";
|
|
38
|
+
import { contractOf } from "./service/api.js";
|
|
39
|
+
import { spineFindings } from "./service/spine.js";
|
|
40
|
+
/**
|
|
41
|
+
* The landscape target's findings that grade a file other than the map, with
|
|
42
|
+
* the file — or directory — that carries each.
|
|
43
|
+
*
|
|
44
|
+
* Only the families that are a VERDICT ON THAT FILE are listed: the vocabulary
|
|
45
|
+
* that does not read, and the declaration nothing cites. The obligation codes
|
|
46
|
+
* the MAP causes stay off this table — `obligation.unknown` (a tag naming no
|
|
47
|
+
* rule) and `obligation.uncovered` (an applied rule no requirement covers) are
|
|
48
|
+
* written by an edit to `architecture/`, and a scoped run over that edit owes
|
|
49
|
+
* them. A code on no row rides the landscape target exactly as before.
|
|
50
|
+
*
|
|
51
|
+
* What this costs, said once: the "nothing cites it" grades also move when a
|
|
52
|
+
* CITATION goes away — a branch deleting the last `Requires:` line naming a
|
|
53
|
+
* permission. A branch that touches only a service never graded the landscape
|
|
54
|
+
* at all, so that is not a new blind spot; the unscoped `validate --all` keeps
|
|
55
|
+
* reporting every one of these.
|
|
56
|
+
*/
|
|
57
|
+
const CARRIED = [
|
|
58
|
+
{ carries: (code) => code.startsWith("permissions."), by: (d) => [permissionsPath(d)] },
|
|
59
|
+
{ carries: (code) => code.startsWith("capability."), by: (d) => [capabilitiesPath(d), capabilityDocsDir(d)] },
|
|
60
|
+
{ carries: (code) => code === "obligation.invalid" || code === "obligation.unapplied", by: (d) => [obligationsPath(d)] },
|
|
61
|
+
{ carries: (code) => code.startsWith("glossary."), by: (d) => [glossaryDir(d)] },
|
|
62
|
+
// The stamp is a fact about which binary wrote the file, and a branch that
|
|
63
|
+
// did not edit the file did not move it — `loam doctor` is where a binary
|
|
64
|
+
// upgrade is noticed, not every map PR that happens to follow one.
|
|
65
|
+
{ carries: (code) => code === "agents.stale" || code === "docs.binary-behind", by: (d) => [agentsPath(d)] },
|
|
66
|
+
];
|
|
67
|
+
/** The landscape target's `findings` as a `--base` run reports them. */
|
|
68
|
+
export async function scopedLandscapeFindings(findings, scoped) {
|
|
69
|
+
const { docsDir, scope } = scoped;
|
|
70
|
+
const kept = findings.filter((f) => {
|
|
71
|
+
const carried = CARRIED.find((row) => row.carries(f.code));
|
|
72
|
+
return carried === undefined || carried.by(docsDir).some((abs) => touched(scope, repoPath(docsDir, abs)));
|
|
73
|
+
});
|
|
74
|
+
return [...kept, ...(await changedEdgeFindings(scoped))];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The spine of the edges this branch wrote, graded per provider by the
|
|
78
|
+
* service target's own `spineFindings` over just those edges — so a changed
|
|
79
|
+
* edge reads exactly as it would on the provider's target, and an unreadable
|
|
80
|
+
* or absent contract proves nothing here for the reason it proves nothing
|
|
81
|
+
* there.
|
|
82
|
+
*
|
|
83
|
+
* A provider the run already grades is skipped: its own target walks every
|
|
84
|
+
* inbound edge, these included, and grading them twice would report one broken
|
|
85
|
+
* join as two.
|
|
86
|
+
*/
|
|
87
|
+
async function changedEdgeFindings(scoped) {
|
|
88
|
+
const { docsDir, scope, land, services, fleet } = scoped;
|
|
89
|
+
// A map that does not parse has no edges to read, and `landscape.invalid`
|
|
90
|
+
// on this same target already says so.
|
|
91
|
+
if (land === null || land.errors.length > 0 || scope.edges.size === 0)
|
|
92
|
+
return [];
|
|
93
|
+
const svcOf = serviceResolver(land.elements, new Set(services.map((s) => s.id)));
|
|
94
|
+
const providerOf = (target) => svcOf(target);
|
|
95
|
+
const edges = land.relationships.filter((r) => scope.edges.has(edgeKey(r)));
|
|
96
|
+
const graded = new Set(scope.services.map((s) => s.id));
|
|
97
|
+
const providers = services.filter((s) => !graded.has(s.id) && edges.some((r) => providerOf(r.target) === s.id));
|
|
98
|
+
const perProvider = await inOrder(providers, async (svc) => {
|
|
99
|
+
const paths = servicePathsAt(svc.dir);
|
|
100
|
+
return spineFindings({
|
|
101
|
+
service: svc.id,
|
|
102
|
+
docsDir,
|
|
103
|
+
treePath: serviceTreePath(svc),
|
|
104
|
+
me: svc.id,
|
|
105
|
+
land: { ...land, relationships: edges.filter((r) => providerOf(r.target) === svc.id) },
|
|
106
|
+
landSvcOf: svcOf,
|
|
107
|
+
contract: contractOf(await readOpenapi(paths.openapi, fleet), !existsSync(paths.openapi)),
|
|
108
|
+
landscapeReported: true,
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
return perProvider.flat();
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../../../src/commands/validate/scope.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a `--base` run does to the landscape target, which is the one target\n * whose subject is not a directory.\n *\n * Every other target is graded whole or not at all: a service or a feature the\n * branch touched is graded by exactly the checks `--all` runs on it. The\n * landscape cannot be, for two reasons that pull in opposite directions, and\n * both are defects a docs-repo PR gate actually shipped with.\n *\n * IT GRADED TOO MUCH. The landscape target carries the fleet-wide\n * vocabularies' warnings — a permission nobody requires, a capability nobody\n * realizes, an obligation nobody applies, a glossary term nobody cites — and\n * the AGENTS.md stamp. Every one of those grades a file other than the map, so\n * any `architecture/` branch re-graded all of them, and under `--strict` a\n * one-line map edit on the adopted example fleet went red on four warnings that\n * were all there at the base. Those findings now ride a scoped run only when the\n * file that carries them changed.\n *\n * IT GRADED TOO LITTLE. `spine.op-undefined` is graded on the PROVIDER's\n * service target, so an edge the branch drew or re-spelled on the map was\n * graded by no scoped run unless the provider happened to change too —\n * README's own `authorisePayment` example merged green through both PR gates.\n * Those edges are now graded here, against their providers' contracts, and\n * ONLY those edges: pulling the provider into scope instead would grade every\n * document it already has, which on a spine-first fleet means\n * `service.no-model` reddening exactly the labour that path asks for.\n */\nimport { existsSync } from \"node:fs\";\nimport { serviceResolver } from \"../../core/c4/resolve/service.js\";\nimport type { LoadedDoc } from \"../../core/c4/likec4.js\";\nimport { edgeKey } from \"../../core/diff/scope/edges.js\";\nimport { touched, type ValidateScope } from \"../../core/diff/scope/changed-paths.js\";\nimport { repoPath } from \"../../core/envelope/json.js\";\nimport type { FleetContext } from \"../../core/fleet-context.js\";\nimport { inOrder } from \"../../core/kernel/concurrency.js\";\nimport { serviceTreePath, type DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport { readOpenapi } from \"../../core/openapi/doc.js\";\nimport { capabilityDocsDir, glossaryDir } from \"../../core/repo/authored/paths.js\";\nimport type { ServiceEntry } from \"../../core/repo/entries.js\";\nimport { agentsPath, capabilitiesPath, obligationsPath, permissionsPath, servicePathsAt } from \"../../core/repo/paths.js\";\nimport type { Finding } from \"../../core/vocabulary/report.js\";\nimport { contractOf } from \"./service/api.js\";\nimport { spineFindings } from \"./service/spine.js\";\n\n/**\n * The landscape target's findings that grade a file other than the map, with\n * the file — or directory — that carries each.\n *\n * Only the families that are a VERDICT ON THAT FILE are listed: the vocabulary\n * that does not read, and the declaration nothing cites. The obligation codes\n * the MAP causes stay off this table — `obligation.unknown` (a tag naming no\n * rule) and `obligation.uncovered` (an applied rule no requirement covers) are\n * written by an edit to `architecture/`, and a scoped run over that edit owes\n * them. A code on no row rides the landscape target exactly as before.\n *\n * What this costs, said once: the \"nothing cites it\" grades also move when a\n * CITATION goes away — a branch deleting the last `Requires:` line naming a\n * permission. A branch that touches only a service never graded the landscape\n * at all, so that is not a new blind spot; the unscoped `validate --all` keeps\n * reporting every one of these.\n */\nconst CARRIED: readonly { carries: (code: string) => boolean; by: (docsDir: DocsDir) => string[] }[] = [\n { carries: (code) => code.startsWith(\"permissions.\"), by: (d) => [permissionsPath(d)] },\n { carries: (code) => code.startsWith(\"capability.\"), by: (d) => [capabilitiesPath(d), capabilityDocsDir(d)] },\n { carries: (code) => code === \"obligation.invalid\" || code === \"obligation.unapplied\", by: (d) => [obligationsPath(d)] },\n { carries: (code) => code.startsWith(\"glossary.\"), by: (d) => [glossaryDir(d)] },\n // The stamp is a fact about which binary wrote the file, and a branch that\n // did not edit the file did not move it — `loam doctor` is where a binary\n // upgrade is noticed, not every map PR that happens to follow one.\n { carries: (code) => code === \"agents.stale\" || code === \"docs.binary-behind\", by: (d) => [agentsPath(d)] },\n];\n\n/** What the scoped landscape grade needs from the run. */\nexport interface ScopedLandscape {\n docsDir: DocsDir;\n scope: ValidateScope;\n /** The parsed `architecture/` project, or null when the repo draws no fleet. */\n land: LoadedDoc | null;\n /** The run's FULL service enumeration: a changed edge's provider is found here whether or not it is in scope. */\n services: readonly ServiceEntry[];\n fleet: FleetContext;\n}\n\n/** The landscape target's `findings` as a `--base` run reports them. */\nexport async function scopedLandscapeFindings(findings: Finding[], scoped: ScopedLandscape): Promise<Finding[]> {\n const { docsDir, scope } = scoped;\n const kept = findings.filter((f) => {\n const carried = CARRIED.find((row) => row.carries(f.code));\n return carried === undefined || carried.by(docsDir).some((abs) => touched(scope, repoPath(docsDir, abs)));\n });\n return [...kept, ...(await changedEdgeFindings(scoped))];\n}\n\n/**\n * The spine of the edges this branch wrote, graded per provider by the\n * service target's own `spineFindings` over just those edges — so a changed\n * edge reads exactly as it would on the provider's target, and an unreadable\n * or absent contract proves nothing here for the reason it proves nothing\n * there.\n *\n * A provider the run already grades is skipped: its own target walks every\n * inbound edge, these included, and grading them twice would report one broken\n * join as two.\n */\nasync function changedEdgeFindings(scoped: ScopedLandscape): Promise<Finding[]> {\n const { docsDir, scope, land, services, fleet } = scoped;\n // A map that does not parse has no edges to read, and `landscape.invalid`\n // on this same target already says so.\n if (land === null || land.errors.length > 0 || scope.edges.size === 0) return [];\n const svcOf = serviceResolver(land.elements, new Set(services.map((s) => s.id)));\n const providerOf = (target: string): string => svcOf(target);\n const edges = land.relationships.filter((r) => scope.edges.has(edgeKey(r)));\n const graded = new Set(scope.services.map((s) => s.id));\n const providers = services.filter((s) => !graded.has(s.id) && edges.some((r) => providerOf(r.target) === s.id));\n const perProvider = await inOrder(providers, async (svc) => {\n const paths = servicePathsAt(svc.dir);\n return spineFindings({\n service: svc.id,\n docsDir,\n treePath: serviceTreePath(svc),\n me: svc.id,\n land: { ...land, relationships: edges.filter((r) => providerOf(r.target) === svc.id) },\n landSvcOf: svcOf,\n contract: contractOf(await readOpenapi(paths.openapi, fleet), !existsSync(paths.openapi)),\n landscapeReported: true,\n });\n });\n return perProvider.flat();\n}\n"]}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HTTP contract axis: openapi.yaml graded against the living spec.
|
|
3
|
+
*
|
|
4
|
+
* What it decides for everything downstream is whether there is a contract to
|
|
5
|
+
* read at all. A missing or unparseable openapi.yaml is ONE finding about ONE
|
|
6
|
+
* file, and that fact has to travel: grading the fleet map's inbound edges
|
|
7
|
+
* against the empty operation set it leaves behind turns one broken YAML into
|
|
8
|
+
* one `spine.op-undefined` per consumer, each of them accusing the landscape of
|
|
9
|
+
* a fault that is not there. `ServiceContract` is how it travels; `./spine.js`
|
|
10
|
+
* is what reads it.
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync } from "node:fs";
|
|
13
|
+
import { closeIds } from "../../../core/c4/arch.js";
|
|
14
|
+
import {} from "../../../core/c4/likec4.js";
|
|
15
|
+
import {} from "../../../core/kernel/ids/service.js";
|
|
16
|
+
import {} from "../../../core/repo/paths.js";
|
|
17
|
+
import {} from "../../../core/vocabulary/report.js";
|
|
18
|
+
import { responseGovernanceFindings } from "../checks/requirements.js";
|
|
19
|
+
import {} from "../../../core/document/spec.js";
|
|
20
|
+
import { readOpenapi } from "../../../core/openapi/doc.js";
|
|
21
|
+
import { FleetContext } from "../../../core/fleet-context.js";
|
|
22
|
+
import { repoPath } from "../../../core/envelope/json.js";
|
|
23
|
+
import {} from "../../../core/kernel/ids/dirs.js";
|
|
24
|
+
export async function apiAxisFindings(axis) {
|
|
25
|
+
const { docsDir, service, me, paths, reqs, livingReqs, land, landSvcOf, fleet } = axis;
|
|
26
|
+
const findings = [];
|
|
27
|
+
// API coverage: every operation in openapi.yaml is governed by a requirement.
|
|
28
|
+
const api = await readOpenapi(paths.openapi, fleet);
|
|
29
|
+
const removeMarkers = api.ops.filter((o) => o.remove);
|
|
30
|
+
const liveOps = api.ops.filter((o) => !o.remove);
|
|
31
|
+
/** No contract to read: deleted, renamed, or never written. */
|
|
32
|
+
const contractMissing = !existsSync(paths.openapi);
|
|
33
|
+
const contract = contractOf(api, contractMissing);
|
|
34
|
+
const { ops, deprecated: deprecatedOps } = contract;
|
|
35
|
+
// Inbound calls the landscape can PROVE: op-linked edges whose target
|
|
36
|
+
// resolves to this service. Null when the landscape proves nothing at all
|
|
37
|
+
// (absent, or it did not parse) — which is not the same fact as "nobody
|
|
38
|
+
// calls me", and the two must not be graded alike.
|
|
39
|
+
const inboundOps = land === null || land.errors.length > 0
|
|
40
|
+
? null
|
|
41
|
+
: land.relationships.filter((r) => r.op !== undefined && landSvcOf(r.target) === me);
|
|
42
|
+
// The other half of the evidence a contract is owed: the living spec's own
|
|
43
|
+
// `Operations:` lines. A requirement on its way out governs nothing.
|
|
44
|
+
const governedOps = livingReqs.flatMap((r) => r.operations);
|
|
45
|
+
if (contractMissing) {
|
|
46
|
+
// A contract that is gone is not a reason to stop grading the API axis —
|
|
47
|
+
// it IS the axis's answer, and every check below used to live inside the
|
|
48
|
+
// file-exists branch, so deleting or misspelling the file turned the whole
|
|
49
|
+
// axis (including the documented `spec-api.op-undefined`) green.
|
|
50
|
+
//
|
|
51
|
+
// What decides the severity is whether anything already written down is
|
|
52
|
+
// holding a join into it: a living `Operations:` line, or an op-linked
|
|
53
|
+
// landscape edge. With one of those, the absence breaks a link somebody
|
|
54
|
+
// authored — an error. With neither, this is a service that legitimately
|
|
55
|
+
// has no HTTP surface (a worker nobody calls), and a fleet mid-rollout
|
|
56
|
+
// must not go red for it — a warning, or, when the landscape PROVES nobody
|
|
57
|
+
// calls it, the documented silence.
|
|
58
|
+
//
|
|
59
|
+
// One finding, not one per dangling link: the fix is a single file, and
|
|
60
|
+
// the links it strands ride along as details.
|
|
61
|
+
const dangling = [...new Set([...governedOps, ...(inboundOps ?? []).map((r) => r.op)])].sort();
|
|
62
|
+
if (dangling.length > 0) {
|
|
63
|
+
findings.push({
|
|
64
|
+
severity: "error",
|
|
65
|
+
code: "service.no-openapi",
|
|
66
|
+
subject: service,
|
|
67
|
+
message: `No OpenAPI contract at ${repoPath(docsDir, paths.openapi)}, and ${dangling.length} operation link(s) already point into it — ` +
|
|
68
|
+
`every requirement and landscape edge naming one of them resolves to nothing until the file is back`,
|
|
69
|
+
details: dangling,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
else if (inboundOps === null) {
|
|
73
|
+
findings.push({
|
|
74
|
+
severity: "warn",
|
|
75
|
+
code: "service.no-openapi",
|
|
76
|
+
message: `No OpenAPI contract at ${repoPath(docsDir, paths.openapi)} — API coverage and the landscape spine are unchecked`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else if (api.unreadable) {
|
|
81
|
+
// A contract that EXISTS but does not read is a broken source of truth, not
|
|
82
|
+
// an empty one: swallowing it into zero operations used to grade every
|
|
83
|
+
// inbound landscape edge `spine.op-undefined` — a false diagnosis pointing
|
|
84
|
+
// at the landscape when the truth was this file. So the file is the error,
|
|
85
|
+
// and every check that reads the contract (api.*, the spine's op
|
|
86
|
+
// resolution) is suspended below, the landscape.invalid discipline.
|
|
87
|
+
findings.push({
|
|
88
|
+
severity: "error",
|
|
89
|
+
code: "openapi.invalid",
|
|
90
|
+
message: `${service}: openapi.yaml does not parse — API coverage and the landscape spine are unchecked`,
|
|
91
|
+
...(api.error === undefined ? {} : { details: [api.error] }),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
if (removeMarkers.length > 0) {
|
|
96
|
+
findings.push({
|
|
97
|
+
severity: "error",
|
|
98
|
+
code: "openapi.remove-marker-living",
|
|
99
|
+
message: `${service}: living openapi.yaml contains ${removeMarkers.length} x-loam-remove marker(s) (${removeMarkers.map((op) => op.id).join(", ")}) — removal markers are valid only in feature deltas`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
// The same marker written at PATH level, beside the methods. `readOpenapi`
|
|
103
|
+
// is keyed by (path, method), which is precisely why the check above cannot
|
|
104
|
+
// see this one and precisely how one reached a living contract in the first
|
|
105
|
+
// place: it addresses no operation, so it retired nothing, and it stayed
|
|
106
|
+
// invisible afterwards. Error, like its method-level sibling — a
|
|
107
|
+
// feature-only key published to every consumer of the fleet, and one that
|
|
108
|
+
// keeps the empty-path cleanup from ever firing. Same code as the archive
|
|
109
|
+
// plan's: one breach, one name, wherever it is found.
|
|
110
|
+
for (const removed of api.pathLevelRemovals) {
|
|
111
|
+
findings.push({
|
|
112
|
+
severity: "error",
|
|
113
|
+
code: "openapi.remove-marker-path-level",
|
|
114
|
+
subject: service,
|
|
115
|
+
message: `${service}: living openapi.yaml carries x-loam-remove at PATH level on '${removed}' — ` +
|
|
116
|
+
`a removal marker names ONE operation, so beside the methods it retires nothing and is not a contract key either, ` +
|
|
117
|
+
`and no id-keyed check can see it. Delete it from the living contract; retire an operation through a feature delta whose marker sits inside the operation.`,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// Two slots claiming one operationId in a LIVING contract. `readOpenapi`
|
|
121
|
+
// has computed this since the merge needed it, and only the feature path
|
|
122
|
+
// (coherence) ever read it — so the ambiguity was reachable only when some
|
|
123
|
+
// unrelated feature happened to carry a delta for this service, and the
|
|
124
|
+
// fleet gate that actually runs in CI was blind to it. Same code and same
|
|
125
|
+
// sentence as the feature-scope check: one breach, one name.
|
|
126
|
+
for (const id of api.duplicateIds) {
|
|
127
|
+
const slots = api.ops.filter((op) => op.id === id).map((op) => `${op.method} ${op.path}`);
|
|
128
|
+
findings.push({
|
|
129
|
+
severity: "warn",
|
|
130
|
+
code: "openapi.duplicate-operationid",
|
|
131
|
+
subject: service,
|
|
132
|
+
message: `${service}: the living OpenAPI defines operationId '${id}' at ${slots.join(" and ")} — every join on the id (a requirement's Operations: line, an edge's metadata { op }, a removal marker) picks one of those slots arbitrarily`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
// The contract-depth probes. Form validated and depth did not: an
|
|
136
|
+
// operation whose one response is `description: OK`, and a `$ref` to a
|
|
137
|
+
// schema nobody wrote, both read as cleanly as a complete contract while
|
|
138
|
+
// a reader still cannot rebuild its payloads. Presence
|
|
139
|
+
// probes over the document's own keys; nothing reads what a schema says.
|
|
140
|
+
const undescribed = liveOps.filter((op) => op.undescribed === true).map((op) => op.id);
|
|
141
|
+
if (undescribed.length > 0) {
|
|
142
|
+
findings.push({
|
|
143
|
+
severity: "warn",
|
|
144
|
+
code: "openapi.response-undescribed",
|
|
145
|
+
subject: service,
|
|
146
|
+
message: `${service}: ${undescribed.length} operation(s) declare no response schema (${undescribed.join(", ")}) — ` +
|
|
147
|
+
`the contract names the operation and says nothing about what it returns, so a reader still ` +
|
|
148
|
+
`needs the code; declare content with a schema on at least one response (204/304 excepted)`,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (api.danglingRefs.length > 0) {
|
|
152
|
+
findings.push({
|
|
153
|
+
severity: "warn",
|
|
154
|
+
code: "openapi.ref-unresolved",
|
|
155
|
+
subject: service,
|
|
156
|
+
message: `${service}: openapi.yaml contains ${api.danglingRefs.length} internal $ref(s) that resolve to ` +
|
|
157
|
+
`nothing in the document — the operation validates while its schema is silently absent ` +
|
|
158
|
+
`(the archive plan refuses the same breach in a feature delta, as an error)`,
|
|
159
|
+
details: api.danglingRefs,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
const defined = new Set(ops);
|
|
163
|
+
// `Operations:` on a LIVING requirement, resolved against this service's own
|
|
164
|
+
// contract. Nothing did this before: the same spine is checked inside a
|
|
165
|
+
// feature delta (coherence's spec-api.op-undefined) and then never again, so
|
|
166
|
+
// a typo that shipped, or an operation later renamed out of openapi.yaml,
|
|
167
|
+
// left a living requirement governing an operation that does not exist —
|
|
168
|
+
// green forever, and every downstream join through that id silently empty.
|
|
169
|
+
// Same code and severity as the feature-scope check on purpose: one breach,
|
|
170
|
+
// one name, wherever it is found.
|
|
171
|
+
for (const r of livingReqs) {
|
|
172
|
+
for (const op of r.operations) {
|
|
173
|
+
if (defined.has(op))
|
|
174
|
+
continue;
|
|
175
|
+
const close = closeIds(op, ops);
|
|
176
|
+
findings.push({
|
|
177
|
+
severity: "error",
|
|
178
|
+
code: "spec-api.op-undefined",
|
|
179
|
+
subject: service,
|
|
180
|
+
message: `${service}: requirement '${r.name}' governs '${op}', not defined in ${service}'s OpenAPI` +
|
|
181
|
+
(close.length > 0 ? `. Did you mean: ${close.join(", ")}?` : ""),
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (ops.length > 0) {
|
|
186
|
+
const governed = new Set(livingReqs.flatMap((r) => r.operations));
|
|
187
|
+
const orphans = ops.filter((op) => !governed.has(op));
|
|
188
|
+
if (orphans.length === 0) {
|
|
189
|
+
findings.push({
|
|
190
|
+
severity: "ok",
|
|
191
|
+
code: "api.covered",
|
|
192
|
+
// Both counts on purpose: the ratio is the one signal that lets a
|
|
193
|
+
// reviewer smell a thin baseline from the summary — forty behaviours
|
|
194
|
+
// filed under one requirement passes every check, and "covered" alone
|
|
195
|
+
// read as done.
|
|
196
|
+
message: `${service}: API covered (${ops.length} operation(s) governed by requirements · ${livingReqs.length} living requirement(s))`,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
findings.push({
|
|
201
|
+
severity: "warn",
|
|
202
|
+
code: "api.ungoverned",
|
|
203
|
+
message: `${service}: ${orphans.length} operation(s) not governed by any requirement — ${orphans.join(", ")}`,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
findings.push(...responseGovernanceFindings(service, liveOps, livingReqs));
|
|
207
|
+
// The migration-debt case: requirements exist, the API exists, and no
|
|
208
|
+
// `Operations:` line ties them — every cross-axis check above and in
|
|
209
|
+
// feature mode is vacuously green. Once per service, not per operation;
|
|
210
|
+
// with zero requirements the spec (or its absence) is the finding instead.
|
|
211
|
+
if (reqs.length > 0 && reqs.every((r) => r.operations.length === 0)) {
|
|
212
|
+
findings.push({
|
|
213
|
+
severity: "warn",
|
|
214
|
+
code: "api.ops-unlinked",
|
|
215
|
+
message: `${service}: openapi.yaml defines ${ops.length} operation(s) but no requirement links any — the API axis is unchecked for this service`,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
// Lifecycle: a requirement whose `Operations:` list resolves ONLY to
|
|
219
|
+
// deprecated operations governs behaviour the contract is retiring.
|
|
220
|
+
// Deprecation is the documented first step of removing an op; the explicit
|
|
221
|
+
// feature marker is the final step. Until that delta archives, the op stays
|
|
222
|
+
// live, so the fix is migration or a coordinated retirement. Ops the contract does not define at all
|
|
223
|
+
// prove nothing here and are left to spec-api.op-undefined above.
|
|
224
|
+
for (const r of reqs) {
|
|
225
|
+
const resolved = r.operations.filter((op) => defined.has(op));
|
|
226
|
+
if (resolved.length === 0 || !resolved.every((op) => deprecatedOps.has(op)))
|
|
227
|
+
continue;
|
|
228
|
+
findings.push({
|
|
229
|
+
severity: "warn",
|
|
230
|
+
code: "api.requirement-deprecated",
|
|
231
|
+
message: `${service}: requirement '${r.name}' governs only deprecated operation(s) (${resolved.join(", ")}) — the behaviour it describes is on its way out; migrate it to the replacement operation, or retire it`,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return { findings, contract };
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* What the spine may conclude from one contract read — spelled once, because a
|
|
240
|
+
* `--base` run now grades a changed edge against a provider it does not grade
|
|
241
|
+
* as a target (`../scope.ts`), and two spellings of "which operations are
|
|
242
|
+
* live" would be two chances for a removal marker to count as defined.
|
|
243
|
+
*/
|
|
244
|
+
export function contractOf(api, missing) {
|
|
245
|
+
const live = api.ops.filter((o) => !o.remove);
|
|
246
|
+
return {
|
|
247
|
+
ops: live.map((o) => o.id),
|
|
248
|
+
deprecated: new Set(live.filter((o) => o.deprecated).map((o) => o.id)),
|
|
249
|
+
unreadable: api.unreadable || missing,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../../src/commands/validate/service/api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAkB,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAA8C,MAAM,qCAAqC,CAAC;AACjG,OAAO,EAAqB,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAgB,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAoB,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,WAAW,EAAmB,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAgB,MAAM,kCAAkC,CAAC;AAoChE,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAa;IAEb,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IACvF,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,8EAA8E;IAC9E,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACpD,MAAM,aAAa,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACjD,+DAA+D;IAC/D,MAAM,eAAe,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAClD,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,QAAQ,CAAC;IACpD,sEAAsE;IACtE,0EAA0E;IAC1E,wEAAwE;IACxE,mDAAmD;IACnD,MAAM,UAAU,GACd,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QACrC,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,IAAI,SAAU,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAC1F,2EAA2E;IAC3E,qEAAqE;IACrE,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IAC5D,IAAI,eAAe,EAAE,CAAC;QACpB,yEAAyE;QACzE,yEAAyE;QACzE,2EAA2E;QAC3E,iEAAiE;QACjE,EAAE;QACF,wEAAwE;QACxE,uEAAuE;QACvE,wEAAwE;QACxE,yEAAyE;QACzE,uEAAuE;QACvE,2EAA2E;QAC3E,oCAAoC;QACpC,EAAE;QACF,wEAAwE;QACxE,8CAA8C;QAC9C,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,WAAW,EAAE,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAChG,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,oBAAoB;gBAC1B,OAAO,EAAE,OAAO;gBAChB,OAAO,EACL,0BAA0B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,QAAQ,CAAC,MAAM,6CAA6C;oBAC/H,oGAAoG;gBACtG,OAAO,EAAE,QAAQ;aAClB,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YAC/B,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,MAAM;gBAChB,IAAI,EAAE,oBAAoB;gBAC1B,OAAO,EAAE,0BAA0B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,uDAAuD;aAC3H,CAAC,CAAC;QACL,CAAC;IACH,CAAC;SAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;QAC1B,4EAA4E;QAC5E,uEAAuE;QACvE,2EAA2E;QAC3E,2EAA2E;QAC3E,iEAAiE;QACjE,oEAAoE;QACpE,QAAQ,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,OAAO;YACjB,IAAI,EAAE,iBAAiB;YACvB,OAAO,EAAE,GAAG,OAAO,oFAAoF;YACvG,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;SAC7D,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,8BAA8B;gBACpC,OAAO,EAAE,GAAG,OAAO,kCAAkC,aAAa,CAAC,MAAM,6BAA6B,aAAa,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,sDAAsD;aACxM,CAAC,CAAC;QACL,CAAC;QACD,2EAA2E;QAC3E,4EAA4E;QAC5E,4EAA4E;QAC5E,yEAAyE;QACzE,iEAAiE;QACjE,0EAA0E;QAC1E,0EAA0E;QAC1E,sDAAsD;QACtD,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,iBAAiB,EAAE,CAAC;YAC5C,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,kCAAkC;gBACxC,OAAO,EAAE,OAAO;gBAChB,OAAO,EACL,GAAG,OAAO,iEAAiE,OAAO,MAAM;oBACxF,mHAAmH;oBACnH,2JAA2J;aAC9J,CAAC,CAAC;QACL,CAAC;QACD,yEAAyE;QACzE,yEAAyE;QACzE,2EAA2E;QAC3E,wEAAwE;QACxE,0EAA0E;QAC1E,6DAA6D;QAC7D,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;YAC1F,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,MAAM;gBAChB,IAAI,EAAE,+BAA+B;gBACrC,OAAO,EAAE,OAAO;gBAChB,OAAO,EAAE,GAAG,OAAO,6CAA6C,EAAE,QAAQ,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,8IAA8I;aAC5O,CAAC,CAAC;QACL,CAAC;QACD,kEAAkE;QAClE,uEAAuE;QACvE,yEAAyE;QACzE,uDAAuD;QACvD,yEAAyE;QACzE,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACvF,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,MAAM;gBAChB,IAAI,EAAE,8BAA8B;gBACpC,OAAO,EAAE,OAAO;gBAChB,OAAO,EACL,GAAG,OAAO,KAAK,WAAW,CAAC,MAAM,6CAA6C,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM;oBAC1G,6FAA6F;oBAC7F,2FAA2F;aAC9F,CAAC,CAAC;QACL,CAAC;QACD,IAAI,GAAG,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,MAAM;gBAChB,IAAI,EAAE,wBAAwB;gBAC9B,OAAO,EAAE,OAAO;gBAChB,OAAO,EACL,GAAG,OAAO,2BAA2B,GAAG,CAAC,YAAY,CAAC,MAAM,oCAAoC;oBAChG,wFAAwF;oBACxF,4EAA4E;gBAC9E,OAAO,EAAE,GAAG,CAAC,YAAY;aAC1B,CAAC,CAAC;QACL,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7B,6EAA6E;QAC7E,wEAAwE;QACxE,6EAA6E;QAC7E,0EAA0E;QAC1E,yEAAyE;QACzE,2EAA2E;QAC3E,4EAA4E;QAC5E,kCAAkC;QAClC,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;YAC3B,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;gBAC9B,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;oBAAE,SAAS;gBAC9B,MAAM,KAAK,GAAG,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;gBAChC,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,OAAO;oBACjB,IAAI,EAAE,uBAAuB;oBAC7B,OAAO,EAAE,OAAO;oBAChB,OAAO,EACL,GAAG,OAAO,kBAAkB,CAAC,CAAC,IAAI,cAAc,EAAE,qBAAqB,OAAO,YAAY;wBAC1F,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,mBAAmB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;iBACnE,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;YAClE,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;YACtD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,IAAI;oBACd,IAAI,EAAE,aAAa;oBACnB,kEAAkE;oBAClE,qEAAqE;oBACrE,sEAAsE;oBACtE,gBAAgB;oBAChB,OAAO,EAAE,GAAG,OAAO,kBAAkB,GAAG,CAAC,MAAM,4CAA4C,UAAU,CAAC,MAAM,yBAAyB;iBACtI,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,gBAAgB;oBACtB,OAAO,EAAE,GAAG,OAAO,KAAK,OAAO,CAAC,MAAM,mDAAmD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;iBAC9G,CAAC,CAAC;YACL,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,GAAG,0BAA0B,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;YAC3E,sEAAsE;YACtE,qEAAqE;YACrE,wEAAwE;YACxE,2EAA2E;YAC3E,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;gBACpE,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,kBAAkB;oBACxB,OAAO,EAAE,GAAG,OAAO,0BAA0B,GAAG,CAAC,MAAM,yFAAyF;iBACjJ,CAAC,CAAC;YACL,CAAC;YACD,qEAAqE;YACrE,oEAAoE;YACpE,2EAA2E;YAC3E,4EAA4E;YAC5E,qGAAqG;YACrG,kEAAkE;YAClE,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACrB,MAAM,QAAQ,GAAG,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC9D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBAAE,SAAS;gBACtF,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,4BAA4B;oBAClC,OAAO,EAAE,GAAG,OAAO,kBAAkB,CAAC,CAAC,IAAI,2CAA2C,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,yGAAyG;iBACnN,CAAC,CAAC;YACL,CAAC;QACD,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAChC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,GAAe,EAAE,OAAgB;IAC1D,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC9C,OAAO;QACL,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1B,UAAU,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACtE,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,OAAO;KACtC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The HTTP contract axis: openapi.yaml graded against the living spec.\n *\n * What it decides for everything downstream is whether there is a contract to\n * read at all. A missing or unparseable openapi.yaml is ONE finding about ONE\n * file, and that fact has to travel: grading the fleet map's inbound edges\n * against the empty operation set it leaves behind turns one broken YAML into\n * one `spine.op-undefined` per consumer, each of them accusing the landscape of\n * a fault that is not there. `ServiceContract` is how it travels; `./spine.js`\n * is what reads it.\n */\nimport { existsSync } from \"node:fs\";\nimport { closeIds } from \"../../../core/c4/arch.js\";\nimport { type LoadedDoc } from \"../../../core/c4/likec4.js\";\nimport { type DeclaredService, type PathableService } from \"../../../core/kernel/ids/service.js\";\nimport { type ServicePaths } from \"../../../core/repo/paths.js\";\nimport { type Finding } from \"../../../core/vocabulary/report.js\";\nimport { responseGovernanceFindings } from \"../checks/requirements.js\";\nimport { type Requirement } from \"../../../core/document/spec.js\";\nimport { readOpenapi, type OpenapiDoc } from \"../../../core/openapi/doc.js\";\nimport { FleetContext } from \"../../../core/fleet-context.js\";\nimport { repoPath } from \"../../../core/envelope/json.js\";\nimport { type DocsDir } from \"../../../core/kernel/ids/dirs.js\";\n\n/** What the service target hands the HTTP axis. */\nexport interface ApiAxis {\n /** The docs root, so a message names the contract by its repo-relative path. */\n docsDir: DocsDir;\n service: PathableService;\n /** `service` widened for `===` against the resolver's DOCUMENT text — see service.ts. */\n me: string;\n paths: ServicePaths;\n /** Every requirement the living spec declares, REMOVED ones included. */\n reqs: Requirement[];\n /** The ones that still govern something. */\n livingReqs: Requirement[];\n /** The living landscape, or null when the repo has none. */\n land: LoadedDoc | null;\n /** Its element→service resolver, built once per run; null exactly when `land` is. */\n landSvcOf: ((id: string) => DeclaredService) | null;\n fleet: FleetContext;\n}\n\n/**\n * What the spine may conclude from this service's contract.\n *\n * `unreadable` covers both shapes of \"there is nothing to read\" — absent and\n * unparseable — because they have the same consequence for an inbound edge:\n * the contract proves neither that the operation exists nor that it does not.\n */\nexport interface ServiceContract {\n /** operationIds the living contract defines, empty when there is nothing to read. */\n ops: string[];\n /** Those of them the contract marks `deprecated: true`. */\n deprecated: ReadonlySet<string>;\n unreadable: boolean;\n}\n\nexport async function apiAxisFindings(\n axis: ApiAxis,\n): Promise<{ findings: Finding[]; contract: ServiceContract }> {\n const { docsDir, service, me, paths, reqs, livingReqs, land, landSvcOf, fleet } = axis;\n const findings: Finding[] = [];\n\n // API coverage: every operation in openapi.yaml is governed by a requirement.\n const api = await readOpenapi(paths.openapi, fleet);\n const removeMarkers = api.ops.filter((o) => o.remove);\n const liveOps = api.ops.filter((o) => !o.remove);\n /** No contract to read: deleted, renamed, or never written. */\n const contractMissing = !existsSync(paths.openapi);\n const contract = contractOf(api, contractMissing);\n const { ops, deprecated: deprecatedOps } = contract;\n // Inbound calls the landscape can PROVE: op-linked edges whose target\n // resolves to this service. Null when the landscape proves nothing at all\n // (absent, or it did not parse) — which is not the same fact as \"nobody\n // calls me\", and the two must not be graded alike.\n const inboundOps =\n land === null || land.errors.length > 0\n ? null\n : land.relationships.filter((r) => r.op !== undefined && landSvcOf!(r.target) === me);\n // The other half of the evidence a contract is owed: the living spec's own\n // `Operations:` lines. A requirement on its way out governs nothing.\n const governedOps = livingReqs.flatMap((r) => r.operations);\n if (contractMissing) {\n // A contract that is gone is not a reason to stop grading the API axis —\n // it IS the axis's answer, and every check below used to live inside the\n // file-exists branch, so deleting or misspelling the file turned the whole\n // axis (including the documented `spec-api.op-undefined`) green.\n //\n // What decides the severity is whether anything already written down is\n // holding a join into it: a living `Operations:` line, or an op-linked\n // landscape edge. With one of those, the absence breaks a link somebody\n // authored — an error. With neither, this is a service that legitimately\n // has no HTTP surface (a worker nobody calls), and a fleet mid-rollout\n // must not go red for it — a warning, or, when the landscape PROVES nobody\n // calls it, the documented silence.\n //\n // One finding, not one per dangling link: the fix is a single file, and\n // the links it strands ride along as details.\n const dangling = [...new Set([...governedOps, ...(inboundOps ?? []).map((r) => r.op!)])].sort();\n if (dangling.length > 0) {\n findings.push({\n severity: \"error\",\n code: \"service.no-openapi\",\n subject: service,\n message:\n `No OpenAPI contract at ${repoPath(docsDir, paths.openapi)}, and ${dangling.length} operation link(s) already point into it — ` +\n `every requirement and landscape edge naming one of them resolves to nothing until the file is back`,\n details: dangling,\n });\n } else if (inboundOps === null) {\n findings.push({\n severity: \"warn\",\n code: \"service.no-openapi\",\n message: `No OpenAPI contract at ${repoPath(docsDir, paths.openapi)} — API coverage and the landscape spine are unchecked`,\n });\n }\n } else if (api.unreadable) {\n // A contract that EXISTS but does not read is a broken source of truth, not\n // an empty one: swallowing it into zero operations used to grade every\n // inbound landscape edge `spine.op-undefined` — a false diagnosis pointing\n // at the landscape when the truth was this file. So the file is the error,\n // and every check that reads the contract (api.*, the spine's op\n // resolution) is suspended below, the landscape.invalid discipline.\n findings.push({\n severity: \"error\",\n code: \"openapi.invalid\",\n message: `${service}: openapi.yaml does not parse — API coverage and the landscape spine are unchecked`,\n ...(api.error === undefined ? {} : { details: [api.error] }),\n });\n } else {\n if (removeMarkers.length > 0) {\n findings.push({\n severity: \"error\",\n code: \"openapi.remove-marker-living\",\n message: `${service}: living openapi.yaml contains ${removeMarkers.length} x-loam-remove marker(s) (${removeMarkers.map((op) => op.id).join(\", \")}) — removal markers are valid only in feature deltas`,\n });\n }\n // The same marker written at PATH level, beside the methods. `readOpenapi`\n // is keyed by (path, method), which is precisely why the check above cannot\n // see this one and precisely how one reached a living contract in the first\n // place: it addresses no operation, so it retired nothing, and it stayed\n // invisible afterwards. Error, like its method-level sibling — a\n // feature-only key published to every consumer of the fleet, and one that\n // keeps the empty-path cleanup from ever firing. Same code as the archive\n // plan's: one breach, one name, wherever it is found.\n for (const removed of api.pathLevelRemovals) {\n findings.push({\n severity: \"error\",\n code: \"openapi.remove-marker-path-level\",\n subject: service,\n message:\n `${service}: living openapi.yaml carries x-loam-remove at PATH level on '${removed}' — ` +\n `a removal marker names ONE operation, so beside the methods it retires nothing and is not a contract key either, ` +\n `and no id-keyed check can see it. Delete it from the living contract; retire an operation through a feature delta whose marker sits inside the operation.`,\n });\n }\n // Two slots claiming one operationId in a LIVING contract. `readOpenapi`\n // has computed this since the merge needed it, and only the feature path\n // (coherence) ever read it — so the ambiguity was reachable only when some\n // unrelated feature happened to carry a delta for this service, and the\n // fleet gate that actually runs in CI was blind to it. Same code and same\n // sentence as the feature-scope check: one breach, one name.\n for (const id of api.duplicateIds) {\n const slots = api.ops.filter((op) => op.id === id).map((op) => `${op.method} ${op.path}`);\n findings.push({\n severity: \"warn\",\n code: \"openapi.duplicate-operationid\",\n subject: service,\n message: `${service}: the living OpenAPI defines operationId '${id}' at ${slots.join(\" and \")} — every join on the id (a requirement's Operations: line, an edge's metadata { op }, a removal marker) picks one of those slots arbitrarily`,\n });\n }\n // The contract-depth probes. Form validated and depth did not: an\n // operation whose one response is `description: OK`, and a `$ref` to a\n // schema nobody wrote, both read as cleanly as a complete contract while\n // a reader still cannot rebuild its payloads. Presence\n // probes over the document's own keys; nothing reads what a schema says.\n const undescribed = liveOps.filter((op) => op.undescribed === true).map((op) => op.id);\n if (undescribed.length > 0) {\n findings.push({\n severity: \"warn\",\n code: \"openapi.response-undescribed\",\n subject: service,\n message:\n `${service}: ${undescribed.length} operation(s) declare no response schema (${undescribed.join(\", \")}) — ` +\n `the contract names the operation and says nothing about what it returns, so a reader still ` +\n `needs the code; declare content with a schema on at least one response (204/304 excepted)`,\n });\n }\n if (api.danglingRefs.length > 0) {\n findings.push({\n severity: \"warn\",\n code: \"openapi.ref-unresolved\",\n subject: service,\n message:\n `${service}: openapi.yaml contains ${api.danglingRefs.length} internal $ref(s) that resolve to ` +\n `nothing in the document — the operation validates while its schema is silently absent ` +\n `(the archive plan refuses the same breach in a feature delta, as an error)`,\n details: api.danglingRefs,\n });\n }\n const defined = new Set(ops);\n // `Operations:` on a LIVING requirement, resolved against this service's own\n // contract. Nothing did this before: the same spine is checked inside a\n // feature delta (coherence's spec-api.op-undefined) and then never again, so\n // a typo that shipped, or an operation later renamed out of openapi.yaml,\n // left a living requirement governing an operation that does not exist —\n // green forever, and every downstream join through that id silently empty.\n // Same code and severity as the feature-scope check on purpose: one breach,\n // one name, wherever it is found.\n for (const r of livingReqs) {\n for (const op of r.operations) {\n if (defined.has(op)) continue;\n const close = closeIds(op, ops);\n findings.push({\n severity: \"error\",\n code: \"spec-api.op-undefined\",\n subject: service,\n message:\n `${service}: requirement '${r.name}' governs '${op}', not defined in ${service}'s OpenAPI` +\n (close.length > 0 ? `. Did you mean: ${close.join(\", \")}?` : \"\"),\n });\n }\n }\n if (ops.length > 0) {\n const governed = new Set(livingReqs.flatMap((r) => r.operations));\n const orphans = ops.filter((op) => !governed.has(op));\n if (orphans.length === 0) {\n findings.push({\n severity: \"ok\",\n code: \"api.covered\",\n // Both counts on purpose: the ratio is the one signal that lets a\n // reviewer smell a thin baseline from the summary — forty behaviours\n // filed under one requirement passes every check, and \"covered\" alone\n // read as done.\n message: `${service}: API covered (${ops.length} operation(s) governed by requirements · ${livingReqs.length} living requirement(s))`,\n });\n } else {\n findings.push({\n severity: \"warn\",\n code: \"api.ungoverned\",\n message: `${service}: ${orphans.length} operation(s) not governed by any requirement — ${orphans.join(\", \")}`,\n });\n }\n findings.push(...responseGovernanceFindings(service, liveOps, livingReqs));\n // The migration-debt case: requirements exist, the API exists, and no\n // `Operations:` line ties them — every cross-axis check above and in\n // feature mode is vacuously green. Once per service, not per operation;\n // with zero requirements the spec (or its absence) is the finding instead.\n if (reqs.length > 0 && reqs.every((r) => r.operations.length === 0)) {\n findings.push({\n severity: \"warn\",\n code: \"api.ops-unlinked\",\n message: `${service}: openapi.yaml defines ${ops.length} operation(s) but no requirement links any — the API axis is unchecked for this service`,\n });\n }\n // Lifecycle: a requirement whose `Operations:` list resolves ONLY to\n // deprecated operations governs behaviour the contract is retiring.\n // Deprecation is the documented first step of removing an op; the explicit\n // feature marker is the final step. Until that delta archives, the op stays\n // live, so the fix is migration or a coordinated retirement. Ops the contract does not define at all\n // prove nothing here and are left to spec-api.op-undefined above.\n for (const r of reqs) {\n const resolved = r.operations.filter((op) => defined.has(op));\n if (resolved.length === 0 || !resolved.every((op) => deprecatedOps.has(op))) continue;\n findings.push({\n severity: \"warn\",\n code: \"api.requirement-deprecated\",\n message: `${service}: requirement '${r.name}' governs only deprecated operation(s) (${resolved.join(\", \")}) — the behaviour it describes is on its way out; migrate it to the replacement operation, or retire it`,\n });\n }\n }\n }\n\n return { findings, contract };\n}\n\n/**\n * What the spine may conclude from one contract read — spelled once, because a\n * `--base` run now grades a changed edge against a provider it does not grade\n * as a target (`../scope.ts`), and two spellings of \"which operations are\n * live\" would be two chances for a removal marker to count as defined.\n */\nexport function contractOf(api: OpenapiDoc, missing: boolean): ServiceContract {\n const live = api.ops.filter((o) => !o.remove);\n return {\n ops: live.map((o) => o.id),\n deprecated: new Set(live.filter((o) => o.deprecated).map((o) => o.id)),\n unreadable: api.unreadable || missing,\n };\n}\n"]}
|