@ybotok/loam 0.1.0-beta.3 → 0.2.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2548 -86
- package/COMPARISON.md +80 -63
- package/CONTRIBUTING.md +84 -0
- package/MIGRATING-from-OpenSpec.md +213 -54
- package/QUICKSTART.md +125 -0
- package/README.md +450 -150
- package/ROADMAP.md +494 -0
- package/SCHEMA.md +2355 -256
- package/SECURITY.md +21 -3
- package/WORKFLOW.md +701 -0
- package/dist/cli.js +113 -19
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +209 -0
- package/dist/commands/adopt/adopt.js.map +1 -0
- package/dist/commands/adopt/render.js +268 -0
- package/dist/commands/adopt/render.js.map +1 -0
- package/dist/commands/archive/archive.js +77 -0
- package/dist/commands/archive/archive.js.map +1 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +159 -0
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +317 -0
- package/dist/commands/archive/plan/gate.js.map +1 -0
- package/dist/commands/archive/plan/landscape.js +161 -0
- package/dist/commands/archive/plan/landscape.js.map +1 -0
- package/dist/commands/archive/plan/refusal.js +111 -0
- package/dist/commands/archive/plan/refusal.js.map +1 -0
- package/dist/commands/archive/plan/specs.js +237 -0
- package/dist/commands/archive/plan/specs.js.map +1 -0
- package/dist/commands/archive/plan/state.js +27 -0
- package/dist/commands/archive/plan/state.js.map +1 -0
- package/dist/commands/archive/run.js +251 -0
- package/dist/commands/archive/run.js.map +1 -0
- package/dist/commands/context/context.js +99 -0
- package/dist/commands/context/context.js.map +1 -0
- package/dist/commands/context/print.js +283 -0
- package/dist/commands/context/print.js.map +1 -0
- package/dist/commands/delta/delta.js +279 -0
- package/dist/commands/delta/delta.js.map +1 -0
- package/dist/commands/delta/print.js +205 -0
- package/dist/commands/delta/print.js.map +1 -0
- package/dist/commands/dependencies.js +9 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +93 -0
- package/dist/commands/diff/diff.js.map +1 -0
- package/dist/commands/diff/print.js +38 -0
- package/dist/commands/diff/print.js.map +1 -0
- package/dist/commands/doctor.js +5 -4
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain/explain.js +151 -0
- package/dist/commands/explain/explain.js.map +1 -0
- package/dist/commands/explore.js +29 -24
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +115 -0
- package/dist/commands/gate/gate.js.map +1 -0
- package/dist/commands/gate/print.js +54 -0
- package/dist/commands/gate/print.js.map +1 -0
- package/dist/commands/gherkin/commit.js +146 -0
- package/dist/commands/gherkin/commit.js.map +1 -0
- package/dist/commands/gherkin/gherkin.js +246 -0
- package/dist/commands/gherkin/gherkin.js.map +1 -0
- package/dist/commands/gherkin/reconcile.js +149 -0
- package/dist/commands/gherkin/reconcile.js.map +1 -0
- package/dist/commands/gherkin/render.js +77 -0
- package/dist/commands/gherkin/render.js.map +1 -0
- package/dist/commands/init/example.js +204 -0
- package/dist/commands/init/example.js.map +1 -0
- package/dist/commands/init/first-hour.js +28 -0
- package/dist/commands/init/first-hour.js.map +1 -0
- package/dist/commands/{init.js → init/init.js} +146 -71
- package/dist/commands/init/init.js.map +1 -0
- package/dist/commands/init/mcp.js +131 -0
- package/dist/commands/init/mcp.js.map +1 -0
- package/dist/commands/init/options.js +78 -0
- package/dist/commands/init/options.js.map +1 -0
- package/dist/commands/instructions.js +72 -14
- package/dist/commands/instructions.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +65 -0
- package/dist/commands/list/campaign/campaign.js.map +1 -0
- package/dist/commands/list/campaign/owners.js +65 -0
- package/dist/commands/list/campaign/owners.js.map +1 -0
- package/dist/commands/list/json.js +157 -0
- package/dist/commands/list/json.js.map +1 -0
- package/dist/commands/list/list.js +343 -0
- package/dist/commands/list/list.js.map +1 -0
- package/dist/commands/list/print.js +301 -0
- package/dist/commands/list/print.js.map +1 -0
- package/dist/commands/list/review.js +120 -0
- package/dist/commands/list/review.js.map +1 -0
- package/dist/commands/list/views.js +105 -0
- package/dist/commands/list/views.js.map +1 -0
- package/dist/commands/mcp/dispatch.js +183 -0
- package/dist/commands/mcp/dispatch.js.map +1 -0
- package/dist/commands/mcp/mcp.js +34 -0
- package/dist/commands/mcp/mcp.js.map +1 -0
- package/dist/commands/mcp/serve.js +55 -0
- package/dist/commands/mcp/serve.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/active.js +170 -0
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
- package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/living.js +62 -0
- package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/target.js +219 -0
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/error.js +19 -0
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/read.js +49 -0
- package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
- package/dist/commands/migrate-openspec/print.js +89 -0
- package/dist/commands/migrate-openspec/print.js.map +1 -0
- package/dist/commands/new/commit.js +76 -0
- package/dist/commands/new/commit.js.map +1 -0
- package/dist/commands/new/new.js +283 -0
- package/dist/commands/new/new.js.map +1 -0
- package/dist/commands/new/templates.js +355 -0
- package/dist/commands/new/templates.js.map +1 -0
- package/dist/commands/open.js +203 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/policy/format.js +152 -0
- package/dist/commands/policy/format.js.map +1 -0
- package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
- package/dist/commands/policy/gate.js.map +1 -0
- package/dist/commands/rebase/edit.js +106 -0
- package/dist/commands/rebase/edit.js.map +1 -0
- package/dist/commands/rebase/living.js +185 -0
- package/dist/commands/rebase/living.js.map +1 -0
- package/dist/commands/rebase/plan.js +245 -0
- package/dist/commands/rebase/plan.js.map +1 -0
- package/dist/commands/rebase/rebase.js +275 -0
- package/dist/commands/rebase/rebase.js.map +1 -0
- package/dist/commands/seed/commit.js +192 -0
- package/dist/commands/seed/commit.js.map +1 -0
- package/dist/commands/seed/plan.js +133 -0
- package/dist/commands/seed/plan.js.map +1 -0
- package/dist/commands/seed/seed.js +172 -0
- package/dist/commands/seed/seed.js.map +1 -0
- package/dist/commands/show/feature.js +178 -0
- package/dist/commands/show/feature.js.map +1 -0
- package/dist/commands/show/marks.js +24 -0
- package/dist/commands/show/marks.js.map +1 -0
- package/dist/commands/{show.js → show/service.js} +63 -152
- package/dist/commands/show/service.js.map +1 -0
- package/dist/commands/show/show.js +77 -0
- package/dist/commands/show/show.js.map +1 -0
- package/dist/commands/status/json.js +45 -0
- package/dist/commands/status/json.js.map +1 -0
- package/dist/commands/status/print.js +157 -0
- package/dist/commands/status/print.js.map +1 -0
- package/dist/commands/status/status.js +119 -0
- package/dist/commands/status/status.js.map +1 -0
- package/dist/commands/steps/steps.js +189 -0
- package/dist/commands/steps/steps.js.map +1 -0
- package/dist/commands/subsystem/edit.js +192 -0
- package/dist/commands/subsystem/edit.js.map +1 -0
- package/dist/commands/subsystem/move.js +265 -0
- package/dist/commands/subsystem/move.js.map +1 -0
- package/dist/commands/subsystem/query.js +115 -0
- package/dist/commands/subsystem/query.js.map +1 -0
- package/dist/commands/subsystem/subsystem.js +101 -0
- package/dist/commands/subsystem/subsystem.js.map +1 -0
- package/dist/commands/subsystem/sync.js +103 -0
- package/dist/commands/subsystem/sync.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +94 -0
- package/dist/commands/subsystem/txn/txn.js.map +1 -0
- package/dist/commands/subsystem/txn/views.js +70 -0
- package/dist/commands/subsystem/txn/views.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +160 -0
- package/dist/commands/unarchive/manifest.js.map +1 -0
- package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
- package/dist/commands/unarchive/restore.js.map +1 -0
- package/dist/commands/unarchive/unarchive.js +67 -0
- package/dist/commands/unarchive/unarchive.js.map +1 -0
- package/dist/commands/validate/arch-coverage.js +194 -0
- package/dist/commands/validate/arch-coverage.js.map +1 -0
- package/dist/commands/validate/checks/fleet-shape.js +296 -0
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
- package/dist/commands/validate/checks/health-deps.js +55 -0
- package/dist/commands/validate/checks/health-deps.js.map +1 -0
- package/dist/commands/validate/checks/requirements.js +254 -0
- package/dist/commands/validate/checks/requirements.js.map +1 -0
- package/dist/commands/validate/checks/sources.js +76 -0
- package/dist/commands/validate/checks/sources.js.map +1 -0
- package/dist/commands/validate/checks/vocabulary.js +31 -0
- package/dist/commands/validate/checks/vocabulary.js.map +1 -0
- package/dist/commands/validate/feature.js +216 -0
- package/dist/commands/validate/feature.js.map +1 -0
- package/dist/commands/validate/fleet/census.js +69 -0
- package/dist/commands/validate/fleet/census.js.map +1 -0
- package/dist/commands/validate/fleet/kind-tags.js +39 -0
- package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
- package/dist/commands/validate/fleet/landscape.js +353 -0
- package/dist/commands/validate/fleet/landscape.js.map +1 -0
- package/dist/commands/validate/fleet/load.js +52 -0
- package/dist/commands/validate/fleet/load.js.map +1 -0
- package/dist/commands/validate/fleet/obligations.js +236 -0
- package/dist/commands/validate/fleet/obligations.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/print.js +63 -0
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
- package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/place.js +13 -0
- package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
- package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/steps.js +197 -0
- package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
- package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
- package/dist/commands/validate/fleet/views/ids.js +40 -0
- package/dist/commands/validate/fleet/views/ids.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +59 -0
- package/dist/commands/validate/fleet/views/stale.js.map +1 -0
- package/dist/commands/validate/links/corpus.js +92 -0
- package/dist/commands/validate/links/corpus.js.map +1 -0
- package/dist/commands/validate/links/glossary.js +75 -0
- package/dist/commands/validate/links/glossary.js.map +1 -0
- package/dist/commands/validate/report.js +269 -0
- package/dist/commands/validate/report.js.map +1 -0
- package/dist/commands/validate/service/api.js +338 -0
- package/dist/commands/validate/service/api.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +219 -0
- package/dist/commands/validate/service/events/events.js.map +1 -0
- package/dist/commands/validate/service/events/message-producers.js +77 -0
- package/dist/commands/validate/service/events/message-producers.js.map +1 -0
- package/dist/commands/validate/service/evidence-pins.js +131 -0
- package/dist/commands/validate/service/evidence-pins.js.map +1 -0
- package/dist/commands/validate/service/service.js +250 -0
- package/dist/commands/validate/service/service.js.map +1 -0
- package/dist/commands/validate/service/specs.js +219 -0
- package/dist/commands/validate/service/specs.js.map +1 -0
- package/dist/commands/validate/service/spine.js +113 -0
- package/dist/commands/validate/service/spine.js.map +1 -0
- package/dist/commands/validate/validate.js +338 -0
- package/dist/commands/validate/validate.js.map +1 -0
- package/dist/commands/verify/cross/diff.js +182 -0
- package/dist/commands/verify/cross/diff.js.map +1 -0
- package/dist/commands/verify/evidence/contract.js +32 -0
- package/dist/commands/verify/evidence/contract.js.map +1 -0
- package/dist/commands/verify/evidence/pins.js +52 -0
- package/dist/commands/verify/evidence/pins.js.map +1 -0
- package/dist/commands/verify/evidence/read.js +72 -0
- package/dist/commands/verify/evidence/read.js.map +1 -0
- package/dist/commands/verify/frozen.js +241 -0
- package/dist/commands/verify/frozen.js.map +1 -0
- package/dist/commands/verify/record.js +226 -0
- package/dist/commands/verify/record.js.map +1 -0
- package/dist/commands/verify/report.js +223 -0
- package/dist/commands/verify/report.js.map +1 -0
- package/dist/commands/verify/results.js +246 -0
- package/dist/commands/verify/results.js.map +1 -0
- package/dist/commands/verify/verify.js +217 -0
- package/dist/commands/verify/verify.js.map +1 -0
- package/dist/commands/vouch/contract.js +11 -0
- package/dist/commands/vouch/contract.js.map +1 -0
- package/dist/commands/vouch/pack/contract.js +2 -0
- package/dist/commands/vouch/pack/contract.js.map +1 -0
- package/dist/commands/vouch/pack/pack.js +266 -0
- package/dist/commands/vouch/pack/pack.js.map +1 -0
- package/dist/commands/vouch/pack/print.js +271 -0
- package/dist/commands/vouch/pack/print.js.map +1 -0
- package/dist/commands/vouch/pack/sections.js +142 -0
- package/dist/commands/vouch/pack/sections.js.map +1 -0
- package/dist/commands/vouch/prompt/confirm.js +46 -0
- package/dist/commands/vouch/prompt/confirm.js.map +1 -0
- package/dist/commands/vouch/report.js +101 -0
- package/dist/commands/vouch/report.js.map +1 -0
- package/dist/commands/vouch/run.js +285 -0
- package/dist/commands/vouch/run.js.map +1 -0
- package/dist/commands/vouch/sample/plan.js +125 -0
- package/dist/commands/vouch/sample/plan.js.map +1 -0
- package/dist/commands/vouch/sample/print.js +109 -0
- package/dist/commands/vouch/sample/print.js.map +1 -0
- package/dist/commands/vouch/vet/verify.js +123 -0
- package/dist/commands/vouch/vet/verify.js.map +1 -0
- package/dist/commands/vouch/vouch.js +176 -0
- package/dist/commands/vouch/vouch.js.map +1 -0
- package/dist/core/agent/agents-md/artifacts.js +220 -0
- package/dist/core/agent/agents-md/artifacts.js.map +1 -0
- package/dist/core/agent/agents-md/command-map.js +324 -0
- package/dist/core/agent/agents-md/command-map.js.map +1 -0
- package/dist/core/agent/agents-md/cycle.js +165 -0
- package/dist/core/agent/agents-md/cycle.js.map +1 -0
- package/dist/core/agent/agents-md/map/explain.js +34 -0
- package/dist/core/agent/agents-md/map/explain.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
- package/dist/core/agent/agents-md/map/mcp.js +41 -0
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
- package/dist/core/agent/agents-md/map/status.js +62 -0
- package/dist/core/agent/agents-md/map/status.js.map +1 -0
- package/dist/core/agent/agents-md/map/subsystem.js +32 -0
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
- package/dist/core/agent/agents-md/map/usecases.js +60 -0
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
- package/dist/core/agent/agents-md/refusals.js +246 -0
- package/dist/core/agent/agents-md/refusals.js.map +1 -0
- package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
- package/dist/core/agent/agents-md/spine.js +40 -0
- package/dist/core/agent/agents-md/spine.js.map +1 -0
- package/dist/core/agent/agents-md.js +170 -0
- package/dist/core/agent/agents-md.js.map +1 -0
- package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
- package/dist/core/agent/agents-stamp.js.map +1 -0
- package/dist/core/agent/contract.js +2 -0
- package/dist/core/agent/contract.js.map +1 -0
- package/dist/core/agent/protocol.js +193 -0
- package/dist/core/agent/protocol.js.map +1 -0
- package/dist/core/agent/scaffold.js +259 -0
- package/dist/core/agent/scaffold.js.map +1 -0
- package/dist/core/agent/support/report.js +103 -0
- package/dist/core/agent/support/report.js.map +1 -0
- package/dist/core/agent/tools/dialects.js +150 -0
- package/dist/core/agent/tools/dialects.js.map +1 -0
- package/dist/core/agent/tools/registry.js +228 -0
- package/dist/core/agent/tools/registry.js.map +1 -0
- package/dist/core/agent/workflows/adopt.js +133 -0
- package/dist/core/agent/workflows/adopt.js.map +1 -0
- package/dist/core/agent/workflows/check.js +334 -0
- package/dist/core/agent/workflows/check.js.map +1 -0
- package/dist/core/agent/workflows/closing.js +262 -0
- package/dist/core/agent/workflows/closing.js.map +1 -0
- package/dist/core/agent/workflows/feature.js +105 -0
- package/dist/core/agent/workflows/feature.js.map +1 -0
- package/dist/core/agent/workflows/implement.js +105 -0
- package/dist/core/agent/workflows/implement.js.map +1 -0
- package/dist/core/agent/workflows/reference/authoring.js +209 -0
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
- package/dist/core/agent/workflows/reference/codes.js +65 -0
- package/dist/core/agent/workflows/reference/codes.js.map +1 -0
- package/dist/core/agent/workflows/reference/done-check.js +188 -0
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
- package/dist/core/agent/workflows/reference/reference.js +16 -0
- package/dist/core/agent/workflows/reference/reference.js.map +1 -0
- package/dist/core/agent/workflows/reference/spine.js +297 -0
- package/dist/core/agent/workflows/reference/spine.js.map +1 -0
- package/dist/core/asyncapi/baseline/plan.js +110 -0
- package/dist/core/asyncapi/baseline/plan.js.map +1 -0
- package/dist/core/asyncapi/baseline/surfaces.js +54 -0
- package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
- package/dist/core/asyncapi/depth.js +164 -0
- package/dist/core/asyncapi/depth.js.map +1 -0
- package/dist/core/asyncapi/digest.js +141 -0
- package/dist/core/asyncapi/digest.js.map +1 -0
- package/dist/core/asyncapi/merge/components.js +91 -0
- package/dist/core/asyncapi/merge/components.js.map +1 -0
- package/dist/core/asyncapi/merge/error.js +33 -0
- package/dist/core/asyncapi/merge/error.js.map +1 -0
- package/dist/core/asyncapi/merge/markers.js +308 -0
- package/dist/core/asyncapi/merge/markers.js.map +1 -0
- package/dist/core/asyncapi/merge/merge.js +215 -0
- package/dist/core/asyncapi/merge/merge.js.map +1 -0
- package/dist/core/asyncapi/merge/pin.js +107 -0
- package/dist/core/asyncapi/merge/pin.js.map +1 -0
- package/dist/core/asyncapi/model.js +11 -0
- package/dist/core/asyncapi/model.js.map +1 -0
- package/dist/core/asyncapi/producers.js +50 -0
- package/dist/core/asyncapi/producers.js.map +1 -0
- package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
- package/dist/core/asyncapi/read.js.map +1 -0
- package/dist/core/brief/brief.js +225 -0
- package/dist/core/brief/brief.js.map +1 -0
- package/dist/core/brief/checks.js +250 -0
- package/dist/core/brief/checks.js.map +1 -0
- package/dist/core/brief/landscape.js +194 -0
- package/dist/core/brief/landscape.js.map +1 -0
- package/dist/core/brief/targets.js +226 -0
- package/dist/core/brief/targets.js.map +1 -0
- package/dist/core/brief/unchecked.js +47 -0
- package/dist/core/brief/unchecked.js.map +1 -0
- package/dist/core/c4/arch.js +282 -0
- package/dist/core/c4/arch.js.map +1 -0
- package/dist/core/c4/likec4.js +88 -0
- package/dist/core/c4/likec4.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js +132 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
- package/dist/core/c4/parsed/specification.js +78 -0
- package/dist/core/c4/parsed/specification.js.map +1 -0
- package/dist/core/c4/parsed/values.js +57 -0
- package/dist/core/c4/parsed/values.js.map +1 -0
- package/dist/core/c4/parsed/view-ids.js +75 -0
- package/dist/core/c4/parsed/view-ids.js.map +1 -0
- package/dist/core/c4/project/architecture.js +45 -0
- package/dist/core/c4/project/architecture.js.map +1 -0
- package/dist/core/c4/project/documents.js +58 -0
- package/dist/core/c4/project/documents.js.map +1 -0
- package/dist/core/c4/project/load.js +183 -0
- package/dist/core/c4/project/load.js.map +1 -0
- package/dist/core/c4/resolve/service.js +113 -0
- package/dist/core/c4/resolve/service.js.map +1 -0
- package/dist/core/c4/seed/fleet-file.js +201 -0
- package/dist/core/c4/seed/fleet-file.js.map +1 -0
- package/dist/core/c4/seed/items.js +91 -0
- package/dist/core/c4/seed/items.js.map +1 -0
- package/dist/core/c4/seed/stamp.js +48 -0
- package/dist/core/c4/seed/stamp.js.map +1 -0
- package/dist/core/c4/seed/template.js +109 -0
- package/dist/core/c4/seed/template.js.map +1 -0
- package/dist/core/c4/source-mask.js +82 -0
- package/dist/core/c4/source-mask.js.map +1 -0
- package/dist/core/c4/source-scan.js +227 -0
- package/dist/core/c4/source-scan.js.map +1 -0
- package/dist/core/c4/splice/authored-source.js +101 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -0
- package/dist/core/c4/splice/contract.js +10 -0
- package/dist/core/c4/splice/contract.js.map +1 -0
- package/dist/core/c4/splice/delta-blocks.js +126 -0
- package/dist/core/c4/splice/delta-blocks.js.map +1 -0
- package/dist/core/c4/splice/identity/edges.js +36 -0
- package/dist/core/c4/splice/identity/edges.js.map +1 -0
- package/dist/core/c4/splice/landscape-merge.js +269 -0
- package/dist/core/c4/splice/landscape-merge.js.map +1 -0
- package/dist/core/c4/splice/placement.js +194 -0
- package/dist/core/c4/splice/placement.js.map +1 -0
- package/dist/core/c4/workspace.js +172 -0
- package/dist/core/c4/workspace.js.map +1 -0
- package/dist/core/capabilities/authoring/notes.js +141 -0
- package/dist/core/capabilities/authoring/notes.js.map +1 -0
- package/dist/core/capabilities/capabilities.js +124 -0
- package/dist/core/capabilities/capabilities.js.map +1 -0
- package/dist/core/capabilities/delta/doc.js +50 -0
- package/dist/core/capabilities/delta/doc.js.map +1 -0
- package/dist/core/capabilities/delta/overlay.js +129 -0
- package/dist/core/capabilities/delta/overlay.js.map +1 -0
- package/dist/core/capabilities/delta/summary.js +57 -0
- package/dist/core/capabilities/delta/summary.js.map +1 -0
- package/dist/core/capabilities/delta/tree.js +43 -0
- package/dist/core/capabilities/delta/tree.js.map +1 -0
- package/dist/core/capabilities/delta/uncovered.js +222 -0
- package/dist/core/capabilities/delta/uncovered.js.map +1 -0
- package/dist/core/capabilities/findings.js +342 -0
- package/dist/core/capabilities/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/findings.js +181 -0
- package/dist/core/capabilities/realizes/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/join.js +143 -0
- package/dist/core/capabilities/realizes/join.js.map +1 -0
- package/dist/core/capabilities/rollup.js +199 -0
- package/dist/core/capabilities/rollup.js.map +1 -0
- package/dist/core/capabilities/tree.js +126 -0
- package/dist/core/capabilities/tree.js.map +1 -0
- package/dist/core/capabilities/usecase-join.js +277 -0
- package/dist/core/capabilities/usecase-join.js.map +1 -0
- package/dist/core/coherence/authoring/scaffold.js +177 -0
- package/dist/core/coherence/authoring/scaffold.js.map +1 -0
- package/dist/core/coherence/authoring/sentinels.js +74 -0
- package/dist/core/coherence/authoring/sentinels.js.map +1 -0
- package/dist/core/coherence/coherence.js +251 -0
- package/dist/core/coherence/coherence.js.map +1 -0
- package/dist/core/coherence/declared.js +269 -0
- package/dist/core/coherence/declared.js.map +1 -0
- package/dist/core/coherence/events/declared.js +206 -0
- package/dist/core/coherence/events/declared.js.map +1 -0
- package/dist/core/coherence/events/events.js +204 -0
- package/dist/core/coherence/events/events.js.map +1 -0
- package/dist/core/coherence/events/grades.js +166 -0
- package/dist/core/coherence/events/grades.js.map +1 -0
- package/dist/core/coherence/events/lookups.js +122 -0
- package/dist/core/coherence/events/lookups.js.map +1 -0
- package/dist/core/coherence/events/merged.js +40 -0
- package/dist/core/coherence/events/merged.js.map +1 -0
- package/dist/core/coherence/living.js +198 -0
- package/dist/core/coherence/living.js.map +1 -0
- package/dist/core/coherence/lookups.js +155 -0
- package/dist/core/coherence/lookups.js.map +1 -0
- package/dist/core/coherence/pending.js +39 -0
- package/dist/core/coherence/pending.js.map +1 -0
- package/dist/core/conflict-markers.js +60 -0
- package/dist/core/conflict-markers.js.map +1 -0
- package/dist/core/delta/claims.js +73 -0
- package/dist/core/delta/claims.js.map +1 -0
- package/dist/core/delta/delta.js +296 -0
- package/dist/core/delta/delta.js.map +1 -0
- package/dist/core/delta/document.js +177 -0
- package/dist/core/delta/document.js.map +1 -0
- package/dist/core/delta/scope.js +48 -0
- package/dist/core/delta/scope.js.map +1 -0
- package/dist/core/delta/select.js +237 -0
- package/dist/core/delta/select.js.map +1 -0
- package/dist/core/dependencies/dependencies.js +141 -0
- package/dist/core/dependencies/dependencies.js.map +1 -0
- package/dist/core/dependencies/facts.js +165 -0
- package/dist/core/dependencies/facts.js.map +1 -0
- package/dist/core/dependencies/fanin.js +88 -0
- package/dist/core/dependencies/fanin.js.map +1 -0
- package/dist/core/dependencies/graph.js +106 -0
- package/dist/core/dependencies/graph.js.map +1 -0
- package/dist/core/diff/base-git.js +148 -0
- package/dist/core/diff/base-git.js.map +1 -0
- package/dist/core/diff/base-state.js +209 -0
- package/dist/core/diff/base-state.js.map +1 -0
- package/dist/core/diff/current-state.js +96 -0
- package/dist/core/diff/current-state.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +188 -0
- package/dist/core/diff/scope/changed-paths.js.map +1 -0
- package/dist/core/diff/semantic.js +243 -0
- package/dist/core/diff/semantic.js.map +1 -0
- package/dist/core/diff/victims.js +247 -0
- package/dist/core/diff/victims.js.map +1 -0
- package/dist/core/docs.js +7 -64
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +222 -0
- package/dist/core/doctor/agents.js.map +1 -0
- package/dist/core/doctor/config.js +135 -0
- package/dist/core/doctor/config.js.map +1 -0
- package/dist/core/doctor/doctor.js +275 -0
- package/dist/core/doctor/doctor.js.map +1 -0
- package/dist/core/doctor/report.js +13 -0
- package/dist/core/doctor/report.js.map +1 -0
- package/dist/core/doctor/residue.js +110 -0
- package/dist/core/doctor/residue.js.map +1 -0
- package/dist/core/document/apply.js +77 -0
- package/dist/core/document/apply.js.map +1 -0
- package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
- package/dist/core/document/frontmatter.js.map +1 -0
- package/dist/core/document/grammar/directives.js +143 -0
- package/dist/core/document/grammar/directives.js.map +1 -0
- package/dist/core/document/parse.js +263 -0
- package/dist/core/document/parse.js.map +1 -0
- package/dist/core/document/scenarios.js +221 -0
- package/dist/core/document/scenarios.js.map +1 -0
- package/dist/core/document/spec.js +241 -0
- package/dist/core/document/spec.js.map +1 -0
- package/dist/core/{config.js → envelope/config.js} +79 -18
- package/dist/core/envelope/config.js.map +1 -0
- package/dist/core/envelope/json.js +175 -0
- package/dist/core/envelope/json.js.map +1 -0
- package/dist/core/{version.js → envelope/version.js} +3 -3
- package/dist/core/envelope/version.js.map +1 -0
- package/dist/core/explain/families.js +175 -0
- package/dist/core/explain/families.js.map +1 -0
- package/dist/core/explain/fix-tables.js +178 -0
- package/dist/core/explain/fix-tables.js.map +1 -0
- package/dist/core/explain/lookup.js +181 -0
- package/dist/core/explain/lookup.js.map +1 -0
- package/dist/core/explain/refusals.js +49 -0
- package/dist/core/explain/refusals.js.map +1 -0
- package/dist/core/explain/terms.js +144 -0
- package/dist/core/explain/terms.js.map +1 -0
- package/dist/core/explore/describe.js +105 -0
- package/dist/core/explore/describe.js.map +1 -0
- package/dist/core/{explore.js → explore/explore.js} +50 -101
- package/dist/core/explore/explore.js.map +1 -0
- package/dist/core/fleet-context.js +120 -78
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +201 -0
- package/dist/core/gate/checks.js.map +1 -0
- package/dist/core/gate/partners.js +205 -0
- package/dist/core/gate/partners.js.map +1 -0
- package/dist/core/gate/report.js +47 -0
- package/dist/core/gate/report.js.map +1 -0
- package/dist/core/gate/verification.js +113 -0
- package/dist/core/gate/verification.js.map +1 -0
- package/dist/core/gherkin/digest.js +60 -0
- package/dist/core/gherkin/digest.js.map +1 -0
- package/dist/core/gherkin/emit.js +177 -0
- package/dist/core/gherkin/emit.js.map +1 -0
- package/dist/core/gherkin/read.js +76 -0
- package/dist/core/gherkin/read.js.map +1 -0
- package/dist/core/gherkin/stale.js +234 -0
- package/dist/core/gherkin/stale.js.map +1 -0
- package/dist/core/gherkin/stamp.js +77 -0
- package/dist/core/gherkin/stamp.js.map +1 -0
- package/dist/core/gherkin/steps/catalogue.js +154 -0
- package/dist/core/gherkin/steps/catalogue.js.map +1 -0
- package/dist/core/gherkin/steps/inventory.js +98 -0
- package/dist/core/gherkin/steps/inventory.js.map +1 -0
- package/dist/core/gherkin/steps/phrase.js +72 -0
- package/dist/core/gherkin/steps/phrase.js.map +1 -0
- package/dist/core/glossary/delta.js +106 -0
- package/dist/core/glossary/delta.js.map +1 -0
- package/dist/core/glossary/tree.js +86 -0
- package/dist/core/glossary/tree.js.map +1 -0
- package/dist/core/kernel/concurrency.js.map +1 -0
- package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
- package/dist/core/kernel/document-bytes.js.map +1 -0
- package/dist/core/kernel/fences.js +42 -0
- package/dist/core/kernel/fences.js.map +1 -0
- package/dist/core/kernel/ids/capability.js +72 -0
- package/dist/core/kernel/ids/capability.js.map +1 -0
- package/dist/core/kernel/ids/dirs.js +33 -0
- package/dist/core/kernel/ids/dirs.js.map +1 -0
- package/dist/core/kernel/ids/feature.js +75 -0
- package/dist/core/kernel/ids/feature.js.map +1 -0
- package/dist/core/kernel/ids/service.js +156 -0
- package/dist/core/kernel/ids/service.js.map +1 -0
- package/dist/core/kernel/ids/subsystem.js +63 -0
- package/dist/core/kernel/ids/subsystem.js.map +1 -0
- package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
- package/dist/core/kernel/path-safety.js.map +1 -0
- package/dist/core/kernel/records.js.map +1 -0
- package/dist/core/links/backlinks.js +68 -0
- package/dist/core/links/backlinks.js.map +1 -0
- package/dist/core/links/case.js +74 -0
- package/dist/core/links/case.js.map +1 -0
- package/dist/core/links/corpus.js +104 -0
- package/dist/core/links/corpus.js.map +1 -0
- package/dist/core/links/findings.js +110 -0
- package/dist/core/links/findings.js.map +1 -0
- package/dist/core/links/parse.js +236 -0
- package/dist/core/links/parse.js.map +1 -0
- package/dist/core/mcp/argv.js +111 -0
- package/dist/core/mcp/argv.js.map +1 -0
- package/dist/core/mcp/author-tools.js +72 -0
- package/dist/core/mcp/author-tools.js.map +1 -0
- package/dist/core/mcp/framing.js +46 -0
- package/dist/core/mcp/framing.js.map +1 -0
- package/dist/core/mcp/protocol.js +280 -0
- package/dist/core/mcp/protocol.js.map +1 -0
- package/dist/core/mcp/tools.js +314 -0
- package/dist/core/mcp/tools.js.map +1 -0
- package/dist/core/obligations/obligations.js +106 -0
- package/dist/core/obligations/obligations.js.map +1 -0
- package/dist/core/openapi/baseline/gate.js +166 -0
- package/dist/core/openapi/baseline/gate.js.map +1 -0
- package/dist/core/openapi/baseline/plan.js +112 -0
- package/dist/core/openapi/baseline/plan.js.map +1 -0
- package/dist/core/openapi/baseline/record.js +175 -0
- package/dist/core/openapi/baseline/record.js.map +1 -0
- package/dist/core/openapi/depth.js +122 -0
- package/dist/core/openapi/depth.js.map +1 -0
- package/dist/core/openapi/digest.js +189 -0
- package/dist/core/openapi/digest.js.map +1 -0
- package/dist/core/{openapi.js → openapi/doc.js} +44 -106
- package/dist/core/openapi/doc.js.map +1 -0
- package/dist/core/openapi/merge/components.js +234 -0
- package/dist/core/openapi/merge/components.js.map +1 -0
- package/dist/core/openapi/merge/error.js +26 -0
- package/dist/core/openapi/merge/error.js.map +1 -0
- package/dist/core/openapi/merge/markers.js +208 -0
- package/dist/core/openapi/merge/markers.js.map +1 -0
- package/dist/core/openapi/merge/merge.js +256 -0
- package/dist/core/openapi/merge/merge.js.map +1 -0
- package/dist/core/openapi/merge/pin.js +128 -0
- package/dist/core/openapi/merge/pin.js.map +1 -0
- package/dist/core/openspec/decide/capabilities.js +244 -0
- package/dist/core/openspec/decide/capabilities.js.map +1 -0
- package/dist/core/openspec/decide/dispositions.js +63 -0
- package/dist/core/openspec/decide/dispositions.js.map +1 -0
- package/dist/core/openspec/decide/renames.js +179 -0
- package/dist/core/openspec/decide/renames.js.map +1 -0
- package/dist/core/openspec/decide/verdict.js +84 -0
- package/dist/core/openspec/decide/verdict.js.map +1 -0
- package/dist/core/openspec/inventory.js +221 -0
- package/dist/core/openspec/inventory.js.map +1 -0
- package/dist/core/openspec/model/mapping.js +76 -0
- package/dist/core/openspec/model/mapping.js.map +1 -0
- package/dist/core/openspec/model/model.js +65 -0
- package/dist/core/openspec/model/model.js.map +1 -0
- package/dist/core/openspec/scan/changes.js +235 -0
- package/dist/core/openspec/scan/changes.js.map +1 -0
- package/dist/core/openspec/scan/config.js +200 -0
- package/dist/core/openspec/scan/config.js.map +1 -0
- package/dist/core/openspec/scan/shape.js +260 -0
- package/dist/core/openspec/scan/shape.js.map +1 -0
- package/dist/core/openspec/scan/walk.js +133 -0
- package/dist/core/openspec/scan/walk.js.map +1 -0
- package/dist/core/openspec/scan/workspace.js +71 -0
- package/dist/core/openspec/scan/workspace.js.map +1 -0
- package/dist/core/owners/codeowners.js +141 -0
- package/dist/core/owners/codeowners.js.map +1 -0
- package/dist/core/pack/features.js +120 -0
- package/dist/core/pack/features.js.map +1 -0
- package/dist/core/pack/joins.js +112 -0
- package/dist/core/pack/joins.js.map +1 -0
- package/dist/core/pack/living.js +118 -0
- package/dist/core/pack/living.js.map +1 -0
- package/dist/core/pack/pack.js +76 -0
- package/dist/core/pack/pack.js.map +1 -0
- package/dist/core/permissions/permissions.js +87 -0
- package/dist/core/permissions/permissions.js.map +1 -0
- package/dist/core/projection/api.js +84 -0
- package/dist/core/projection/api.js.map +1 -0
- package/dist/core/projection/arch-slice.js +63 -0
- package/dist/core/projection/arch-slice.js.map +1 -0
- package/dist/core/projection/events.js +34 -0
- package/dist/core/projection/events.js.map +1 -0
- package/dist/core/provenance/findings.js +208 -0
- package/dist/core/provenance/findings.js.map +1 -0
- package/dist/core/provenance/git.js +225 -0
- package/dist/core/provenance/git.js.map +1 -0
- package/dist/core/provenance/gitq/moves.js +182 -0
- package/dist/core/provenance/gitq/moves.js.map +1 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
- package/dist/core/provenance/sample/scope.js +238 -0
- package/dist/core/provenance/sample/scope.js.map +1 -0
- package/dist/core/provenance/sample/sections.js +61 -0
- package/dist/core/provenance/sample/sections.js.map +1 -0
- package/dist/core/provenance/sources.js +263 -0
- package/dist/core/provenance/sources.js.map +1 -0
- package/dist/core/provenance/stamp.js +232 -0
- package/dist/core/provenance/stamp.js.map +1 -0
- package/dist/core/provenance/walk.js +157 -0
- package/dist/core/provenance/walk.js.map +1 -0
- package/dist/core/repo/authored/paths.js +192 -0
- package/dist/core/repo/authored/paths.js.map +1 -0
- package/dist/core/repo/entries.js +126 -0
- package/dist/core/repo/entries.js.map +1 -0
- package/dist/core/repo/paths.js +236 -0
- package/dist/core/repo/paths.js.map +1 -0
- package/dist/core/repo/repo.js +248 -0
- package/dist/core/repo/repo.js.map +1 -0
- package/dist/core/repo/service-target.js +153 -0
- package/dist/core/repo/service-target.js.map +1 -0
- package/dist/core/repo/state.js +58 -0
- package/dist/core/repo/state.js.map +1 -0
- package/dist/core/repo/tree/find.js +52 -0
- package/dist/core/repo/tree/find.js.map +1 -0
- package/dist/core/repo/tree/fs.js +64 -0
- package/dist/core/repo/tree/fs.js.map +1 -0
- package/dist/core/repo/tree/marker.js +61 -0
- package/dist/core/repo/tree/marker.js.map +1 -0
- package/dist/core/repo/tree/views.js +172 -0
- package/dist/core/repo/tree/views.js.map +1 -0
- package/dist/core/repo/tree/walk.js +234 -0
- package/dist/core/repo/tree/walk.js.map +1 -0
- package/dist/core/results.js +99 -2
- package/dist/core/results.js.map +1 -1
- package/dist/core/scaffold/landscape.js +131 -0
- package/dist/core/scaffold/landscape.js.map +1 -0
- package/dist/core/scaffold/readme.js +84 -0
- package/dist/core/scaffold/readme.js.map +1 -0
- package/dist/core/staging/commit.js +241 -0
- package/dist/core/staging/commit.js.map +1 -0
- package/dist/core/staging/interrupted.js +96 -0
- package/dist/core/staging/interrupted.js.map +1 -0
- package/dist/core/staging/lock.js +206 -0
- package/dist/core/staging/lock.js.map +1 -0
- package/dist/core/staging/recovery/finding.js +35 -0
- package/dist/core/staging/recovery/finding.js.map +1 -0
- package/dist/core/staging/recovery/intent.js +98 -0
- package/dist/core/staging/recovery/intent.js.map +1 -0
- package/dist/core/staging/recovery/recover.js +126 -0
- package/dist/core/staging/recovery/recover.js.map +1 -0
- package/dist/core/staging/recovery/residue.js +62 -0
- package/dist/core/staging/recovery/residue.js.map +1 -0
- package/dist/core/staging/snapshot.js +202 -0
- package/dist/core/staging/snapshot.js.map +1 -0
- package/dist/core/staging/txn/forward.js +171 -0
- package/dist/core/staging/txn/forward.js.map +1 -0
- package/dist/core/staging/txn/journal.js +139 -0
- package/dist/core/staging/txn/journal.js.map +1 -0
- package/dist/core/staging/txn/transaction.js +75 -0
- package/dist/core/staging/txn/transaction.js.map +1 -0
- package/dist/core/staging/writes.js +113 -0
- package/dist/core/staging/writes.js.map +1 -0
- package/dist/core/status/actions/execution.js +49 -0
- package/dist/core/status/actions/execution.js.map +1 -0
- package/dist/core/status/contracts.js +102 -0
- package/dist/core/status/contracts.js.map +1 -0
- package/dist/core/status/feature/artifacts.js +171 -0
- package/dist/core/status/feature/artifacts.js.map +1 -0
- package/dist/core/status/feature/feature.js +207 -0
- package/dist/core/status/feature/feature.js.map +1 -0
- package/dist/core/status/feature/next.js +150 -0
- package/dist/core/status/feature/next.js.map +1 -0
- package/dist/core/status/feature/state.js +21 -0
- package/dist/core/status/feature/state.js.map +1 -0
- package/dist/core/status/fleet/fleet.js +187 -0
- package/dist/core/status/fleet/fleet.js.map +1 -0
- package/dist/core/status/fleet/next.js +207 -0
- package/dist/core/status/fleet/next.js.map +1 -0
- package/dist/core/status/interrupted.js +97 -0
- package/dist/core/status/interrupted.js.map +1 -0
- package/dist/core/status/report.js +31 -0
- package/dist/core/status/report.js.map +1 -0
- package/dist/core/status/scan.js +41 -0
- package/dist/core/status/scan.js.map +1 -0
- package/dist/core/status/verification.js +201 -0
- package/dist/core/status/verification.js.map +1 -0
- package/dist/core/usecases/capability.js +169 -0
- package/dist/core/usecases/capability.js.map +1 -0
- package/dist/core/usecases/delta/claims.js +116 -0
- package/dist/core/usecases/delta/claims.js.map +1 -0
- package/dist/core/usecases/delta/flows.js +103 -0
- package/dist/core/usecases/delta/flows.js.map +1 -0
- package/dist/core/usecases/delta/overlay.js +235 -0
- package/dist/core/usecases/delta/overlay.js.map +1 -0
- package/dist/core/usecases/fleet.js +137 -0
- package/dist/core/usecases/fleet.js.map +1 -0
- package/dist/core/usecases/operations.js +111 -0
- package/dist/core/usecases/operations.js.map +1 -0
- package/dist/core/usecases/place.js +44 -0
- package/dist/core/usecases/place.js.map +1 -0
- package/dist/core/usecases/touch.js +101 -0
- package/dist/core/usecases/touch.js.map +1 -0
- package/dist/core/verify/answers.js +173 -0
- package/dist/core/verify/answers.js.map +1 -0
- package/dist/core/verify/build.js +150 -0
- package/dist/core/verify/build.js.map +1 -0
- package/dist/core/verify/checklist.js +201 -0
- package/dist/core/verify/checklist.js.map +1 -0
- package/dist/core/verify/claims/identity.js +61 -0
- package/dist/core/verify/claims/identity.js.map +1 -0
- package/dist/core/verify/cross/diff.js +97 -0
- package/dist/core/verify/cross/diff.js.map +1 -0
- package/dist/core/verify/evidence/contract.js +169 -0
- package/dist/core/verify/evidence/contract.js.map +1 -0
- package/dist/core/verify/file.js +257 -0
- package/dist/core/verify/file.js.map +1 -0
- package/dist/core/verify/pins/lint.js +157 -0
- package/dist/core/verify/pins/lint.js.map +1 -0
- package/dist/core/verify/pins/pin.js +95 -0
- package/dist/core/verify/pins/pin.js.map +1 -0
- package/dist/core/verify/record.js +142 -0
- package/dist/core/verify/record.js.map +1 -0
- package/dist/core/verify/store/commit.js +89 -0
- package/dist/core/verify/store/commit.js.map +1 -0
- package/dist/core/verify/store/render.js +57 -0
- package/dist/core/verify/store/render.js.map +1 -0
- package/dist/core/vocabulary/health.js +91 -0
- package/dist/core/vocabulary/health.js.map +1 -0
- package/dist/core/vocabulary/issue.js +55 -0
- package/dist/core/vocabulary/issue.js.map +1 -0
- package/dist/core/vocabulary/maturity.js +145 -0
- package/dist/core/vocabulary/maturity.js.map +1 -0
- package/dist/core/{report.js → vocabulary/report.js} +30 -2
- package/dist/core/vocabulary/report.js.map +1 -0
- package/dist/core/vocabulary/steps.js +266 -0
- package/dist/core/vocabulary/steps.js.map +1 -0
- package/dist/core/workspace/discover.js +239 -0
- package/dist/core/workspace/discover.js.map +1 -0
- package/dist/core/workspace/render.js +52 -0
- package/dist/core/workspace/render.js.map +1 -0
- package/examples/README.md +189 -0
- package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
- package/examples/docs/architecture/capabilities.yaml +31 -0
- package/examples/docs/architecture/landscape.likec4 +280 -0
- package/examples/docs/architecture/obligations.yaml +23 -0
- package/examples/docs/architecture/permissions.yaml +50 -0
- package/examples/docs/architecture/subsystems.likec4 +11 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
- package/examples/docs/capabilities/checkout/spec.md +55 -0
- package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
- package/examples/docs/capabilities/order-notifications/spec.md +43 -0
- package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
- package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
- package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
- package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
- package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
- package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
- package/examples/docs/glossary/order.md +15 -0
- package/examples/docs/glossary/payments/authorization.md +14 -0
- package/examples/docs/likec4.config.json +9 -0
- package/examples/docs/loam.json +3 -0
- package/examples/docs/services/checkout-web/model.likec4 +109 -0
- package/examples/docs/services/checkout-web/spec.md +91 -0
- package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
- package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
- package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
- package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
- package/examples/docs/services/order-service/arch.spec.md +127 -0
- package/examples/docs/services/order-service/asyncapi.yaml +108 -0
- package/examples/docs/services/order-service/health.yaml +57 -0
- package/examples/docs/services/order-service/model.likec4 +138 -0
- package/examples/docs/services/order-service/openapi.yaml +257 -0
- package/examples/docs/services/order-service/runbook.md +62 -0
- package/examples/docs/services/order-service/spec.md +134 -0
- package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
- package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
- package/examples/docs/services/payment-service/arch.spec.md +73 -0
- package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
- package/examples/docs/services/payment-service/health.yaml +39 -0
- package/examples/docs/services/payment-service/model.likec4 +67 -0
- package/examples/docs/services/payment-service/openapi.yaml +151 -0
- package/examples/docs/services/payment-service/runbook.md +37 -0
- package/examples/docs/services/payment-service/spec.md +88 -0
- package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
- package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
- package/examples/docs/services/platform/identity-service/health.yaml +70 -0
- package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
- package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
- package/examples/docs/services/platform/identity-service/runbook.md +95 -0
- package/examples/docs/services/platform/identity-service/spec.md +119 -0
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
- package/examples/docs/services/platform/notification-service/health.yaml +35 -0
- package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
- package/examples/docs/services/platform/notification-service/runbook.md +48 -0
- package/examples/docs/services/platform/notification-service/spec.md +112 -0
- package/examples/docs/services/platform/subsystem.yaml +2 -0
- package/examples/fleet.yaml +57 -0
- package/package.json +19 -6
- package/dist/commands/adopt.js +0 -240
- package/dist/commands/adopt.js.map +0 -1
- package/dist/commands/archive.js +0 -1313
- package/dist/commands/archive.js.map +0 -1
- package/dist/commands/delta.js +0 -388
- package/dist/commands/delta.js.map +0 -1
- package/dist/commands/docs-repo-gate.js.map +0 -1
- package/dist/commands/format.js +0 -17
- package/dist/commands/format.js.map +0 -1
- package/dist/commands/gherkin.js +0 -309
- package/dist/commands/gherkin.js.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/list.js +0 -295
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/migrate-openspec.js +0 -950
- package/dist/commands/migrate-openspec.js.map +0 -1
- package/dist/commands/new.js +0 -413
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/rebase.js +0 -356
- package/dist/commands/rebase.js.map +0 -1
- package/dist/commands/show.js.map +0 -1
- package/dist/commands/status.js +0 -262
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/unarchive.js.map +0 -1
- package/dist/commands/validate.js +0 -1773
- package/dist/commands/validate.js.map +0 -1
- package/dist/commands/verify.js +0 -775
- package/dist/commands/verify.js.map +0 -1
- package/dist/commands/vouch.js +0 -421
- package/dist/commands/vouch.js.map +0 -1
- package/dist/core/agent.js +0 -2173
- package/dist/core/agent.js.map +0 -1
- package/dist/core/agents-stamp.js.map +0 -1
- package/dist/core/arch.js +0 -130
- package/dist/core/arch.js.map +0 -1
- package/dist/core/asyncapi.js.map +0 -1
- package/dist/core/brief.js +0 -755
- package/dist/core/brief.js.map +0 -1
- package/dist/core/coherence.js +0 -577
- package/dist/core/coherence.js.map +0 -1
- package/dist/core/concurrency.js.map +0 -1
- package/dist/core/config.js.map +0 -1
- package/dist/core/delta.js +0 -476
- package/dist/core/delta.js.map +0 -1
- package/dist/core/dependencies.js +0 -356
- package/dist/core/dependencies.js.map +0 -1
- package/dist/core/doctor.js +0 -596
- package/dist/core/doctor.js.map +0 -1
- package/dist/core/document-bytes.js.map +0 -1
- package/dist/core/explore.js.map +0 -1
- package/dist/core/frontmatter.js.map +0 -1
- package/dist/core/gherkin.js +0 -463
- package/dist/core/gherkin.js.map +0 -1
- package/dist/core/health.js +0 -67
- package/dist/core/health.js.map +0 -1
- package/dist/core/ids.js +0 -122
- package/dist/core/ids.js.map +0 -1
- package/dist/core/issue.js +0 -9
- package/dist/core/issue.js.map +0 -1
- package/dist/core/json.js +0 -74
- package/dist/core/json.js.map +0 -1
- package/dist/core/likec4.js +0 -489
- package/dist/core/likec4.js.map +0 -1
- package/dist/core/maturity.js +0 -66
- package/dist/core/maturity.js.map +0 -1
- package/dist/core/openapi-merge.js +0 -552
- package/dist/core/openapi-merge.js.map +0 -1
- package/dist/core/openapi.js.map +0 -1
- package/dist/core/openspec-inventory.js +0 -1440
- package/dist/core/openspec-inventory.js.map +0 -1
- package/dist/core/path-safety.js.map +0 -1
- package/dist/core/provenance.js +0 -787
- package/dist/core/provenance.js.map +0 -1
- package/dist/core/records.js.map +0 -1
- package/dist/core/repo.js +0 -438
- package/dist/core/repo.js.map +0 -1
- package/dist/core/report.js.map +0 -1
- package/dist/core/spec.js +0 -505
- package/dist/core/spec.js.map +0 -1
- package/dist/core/staging.js +0 -890
- package/dist/core/staging.js.map +0 -1
- package/dist/core/status.js +0 -1132
- package/dist/core/status.js.map +0 -1
- package/dist/core/steps.js +0 -72
- package/dist/core/steps.js.map +0 -1
- package/dist/core/verify.js +0 -807
- package/dist/core/verify.js.map +0 -1
- package/dist/core/version.js.map +0 -1
- /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
- /package/dist/core/{records.js → kernel/records.js} +0 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { emitJson, fail } from "../../core/envelope/json.js";
|
|
2
|
+
import { LOAM_VERSION } from "../../core/envelope/version.js";
|
|
3
|
+
import { explainSubject, firstSentence, knownSubjects, listCodes, listTerms, } from "../../core/explain/lookup.js";
|
|
4
|
+
import { nearestIds } from "../../core/repo/entries.js";
|
|
5
|
+
// Interpolated from the registry that owns the terms, never hand-listed: a
|
|
6
|
+
// copied list beside TERMS is a list that silently trails it.
|
|
7
|
+
const TERM_LIST = listTerms()
|
|
8
|
+
.map(({ term }) => term)
|
|
9
|
+
.join(", ");
|
|
10
|
+
/** The listing printed when no subject is asked for — also where codes are said to come from. */
|
|
11
|
+
function printTermListing(json) {
|
|
12
|
+
const terms = listTerms();
|
|
13
|
+
if (json) {
|
|
14
|
+
emitJson({ command: "explain", version: LOAM_VERSION, terms: terms.map(({ term, summary }) => ({ term, description: summary })) });
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
console.log(`loam ${LOAM_VERSION} — the vocabulary\n`);
|
|
18
|
+
const width = Math.max(...terms.map(({ term }) => term.length));
|
|
19
|
+
for (const { term, summary } of terms)
|
|
20
|
+
console.log(` ${term.padEnd(width)} ${summary}`);
|
|
21
|
+
console.log("\nFinding codes come from `loam validate` and `loam verify` output; refusal codes arrive as error.code" +
|
|
22
|
+
"\nin any --json envelope. `loam explain <subject>` explains one.");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* How much of a code's meaning the inventory shows before it stops.
|
|
26
|
+
*
|
|
27
|
+
* The term listing prints whole first sentences because there are six of them
|
|
28
|
+
* and each is under 200 characters. The code listing prints 270-odd rows whose
|
|
29
|
+
* first sentences run to 377 characters, and a row that wraps four times is a
|
|
30
|
+
* paragraph, not a line of a table — it destroys the one thing this listing is
|
|
31
|
+
* for, which is scanning names. Nothing is lost: the sentence is verbatim in
|
|
32
|
+
* `--json`, and `loam explain <code>` prints the whole entry.
|
|
33
|
+
*/
|
|
34
|
+
const SUMMARY_WIDTH = 72;
|
|
35
|
+
/** The listing's one-line gloss: the first sentence of the meaning, cut at a word boundary if it runs long. */
|
|
36
|
+
function summarize(listing) {
|
|
37
|
+
// A code graded in two tables is glossed by the FIRST table's meaning — the
|
|
38
|
+
// listing answers "what is this about", and the per-scope difference (which
|
|
39
|
+
// `explain <code>` prints in full, one block per table) is exactly the
|
|
40
|
+
// detail a one-line row cannot carry honestly.
|
|
41
|
+
const sentence = firstSentence(listing.kind === "finding" ? listing.entries[0].meaning : listing.meaning);
|
|
42
|
+
if (sentence.length <= SUMMARY_WIDTH)
|
|
43
|
+
return sentence;
|
|
44
|
+
const cut = sentence.lastIndexOf(" ", SUMMARY_WIDTH);
|
|
45
|
+
return `${sentence.slice(0, cut === -1 ? SUMMARY_WIDTH : cut)}…`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* `--codes`: the whole code vocabulary, the inventory that was previously
|
|
49
|
+
* enumerable only out of TypeScript unions or the 83 KB /loam-check prose.
|
|
50
|
+
* The JSON rows are the per-code answers verbatim (core/explain/lookup.ts
|
|
51
|
+
* derives `CodeListing` from `Explanation`), so a consumer can build a
|
|
52
|
+
* code-to-fix cache from one call instead of one call per code.
|
|
53
|
+
*/
|
|
54
|
+
function printCodeListing(json) {
|
|
55
|
+
const codes = listCodes();
|
|
56
|
+
if (json) {
|
|
57
|
+
emitJson({ command: "explain", version: LOAM_VERSION, codes });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const findings = codes.filter((listing) => listing.kind === "finding");
|
|
61
|
+
const refusals = codes.filter((listing) => listing.kind === "refusal");
|
|
62
|
+
const width = Math.max(...codes.map(({ code }) => code.length));
|
|
63
|
+
const print = (group) => {
|
|
64
|
+
for (const listing of group)
|
|
65
|
+
console.log(` ${listing.code.padEnd(width)} ${summarize(listing)}`);
|
|
66
|
+
};
|
|
67
|
+
console.log(`loam ${LOAM_VERSION} — the code vocabulary (${findings.length} finding, ${refusals.length} refusal)\n`);
|
|
68
|
+
console.log("finding codes — graded by `loam validate` and `loam verify`, in their findings[]");
|
|
69
|
+
print(findings);
|
|
70
|
+
console.log("\nrefusal codes — the `error.code` of a --json envelope, exit 1");
|
|
71
|
+
print(refusals);
|
|
72
|
+
console.log("\n`loam explain <code>` prints one in full; `loam explain` with no argument lists the concept terms.");
|
|
73
|
+
}
|
|
74
|
+
function printText(subject, explanation) {
|
|
75
|
+
if (explanation.kind === "term") {
|
|
76
|
+
console.log(`${explanation.term}\n\n${explanation.paragraph}`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (explanation.kind === "refusal") {
|
|
80
|
+
console.log(`${subject} — a refusal code (error.code in the --json envelope, exit 1)\n\n${explanation.meaning}`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// A finding: one block per table the code appears in, under the table's own
|
|
84
|
+
// column names, so the page reads as the /loam-check row it is. The
|
|
85
|
+
// severity note repeats on each scope block because it is a PER-TABLE fact:
|
|
86
|
+
// `openapi.ref-unresolved` is a warn under `--service` and an error at
|
|
87
|
+
// archive plan time, and a reader asking "does this block my archive?" must
|
|
88
|
+
// not have to guess which note belongs to which context.
|
|
89
|
+
const severities = [...new Set(explanation.entries.map((entry) => entry.severityNote))];
|
|
90
|
+
console.log(`${subject} (${severities.join("; ")})${explanation.via === undefined ? "" : ` — surfaced by \`${explanation.via}\``}`);
|
|
91
|
+
for (const entry of explanation.entries) {
|
|
92
|
+
console.log(`\n[${entry.scope}] (${entry.severityNote})`);
|
|
93
|
+
console.log(` what it means: ${entry.meaning}`);
|
|
94
|
+
if (entry.fix !== "")
|
|
95
|
+
console.log(` what to do: ${entry.fix}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export function registerExplain(program) {
|
|
99
|
+
program
|
|
100
|
+
.command("explain")
|
|
101
|
+
.argument("[subject]", `a finding code (spine.op-undefined), a refusal code (docs-busy), or a concept term (${TERM_LIST}); omit to list the terms`)
|
|
102
|
+
.description("Explain a finding code, refusal code or concept term, version-matched to this binary")
|
|
103
|
+
.option("--json", "emit the machine contract instead of the human view")
|
|
104
|
+
.option("--codes", "list every finding and refusal code this binary can explain")
|
|
105
|
+
.action((subject, opts) => {
|
|
106
|
+
const json = opts.json === true;
|
|
107
|
+
if (opts.codes === true) {
|
|
108
|
+
// Refused rather than quietly preferring one: `loam explain
|
|
109
|
+
// spine.op-undefined --codes` asks two different questions, and a
|
|
110
|
+
// caller who typed both wanted whichever one this run did NOT pick.
|
|
111
|
+
if (subject !== undefined) {
|
|
112
|
+
fail(json, "invalid-option", `--codes lists the whole vocabulary and takes no subject — drop '${subject}' for the inventory, or drop --codes to explain that one.`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
printCodeListing(json);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (subject === undefined) {
|
|
119
|
+
printTermListing(json);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const explanation = explainSubject(subject);
|
|
123
|
+
if (explanation === null) {
|
|
124
|
+
// The same code `show` and `instructions` use for a name that resolves
|
|
125
|
+
// to nothing, with `nearestIds` suggestions because the known set here
|
|
126
|
+
// is ~200 strings — a caller one typo away should not have to diff the
|
|
127
|
+
// whole vocabulary to find it.
|
|
128
|
+
const near = nearestIds(subject, knownSubjects());
|
|
129
|
+
fail(json, "unknown-target", `Nothing named '${subject}' — not a finding code, a refusal code, or a concept term.` +
|
|
130
|
+
(near.length > 0 ? ` Close: ${near.join(", ")}.` : "") +
|
|
131
|
+
" Run `loam explain` with no argument for the concept list; finding codes appear in `loam validate` and `loam verify` output.");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (json) {
|
|
135
|
+
emitJson({
|
|
136
|
+
command: "explain",
|
|
137
|
+
version: LOAM_VERSION,
|
|
138
|
+
subject,
|
|
139
|
+
kind: explanation.kind,
|
|
140
|
+
...(explanation.kind === "finding"
|
|
141
|
+
? { entries: explanation.entries, ...(explanation.via === undefined ? {} : { via: explanation.via }) }
|
|
142
|
+
: explanation.kind === "refusal"
|
|
143
|
+
? { meaning: explanation.meaning }
|
|
144
|
+
: { term: explanation.term, paragraph: explanation.paragraph }),
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
printText(subject, explanation);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=explain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.js","sourceRoot":"","sources":["../../../src/commands/explain/explain.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `loam explain <code|term>` — what a finding code, a refusal code or a loam\n * concept means, from the binary that emits it.\n *\n * Like `loam instructions` — and for the same reason — this command reads\n * NOTHING: no `loam.json`, no docs repo, no service, and deliberately no\n * docs-repo gate (add-command invariant 3). The vocabulary wall is hit in the\n * first minute in an unfamiliar repository, before or without wiring, and an\n * explanation that refused with `no-config` would be unreachable exactly when\n * a code is first met. The content is version-matched by construction: the\n * finding prose is parsed at runtime out of the same workflow bodies this\n * binary ships (core/explain/fix-tables.ts), so what `explain` says and what\n * /loam-check teaches cannot disagree.\n *\n * `--codes` adds the missing half: per-code lookup was always cheap, but\n * nothing could discover WHICH codes exist — the ~230 finding codes and 46\n * refusal codes were enumerable only from erased TypeScript unions or by\n * parsing the 83 KB prose body of `loam instructions loam-check`. It is opt-in\n * and off by default precisely so the term listing and every per-code page\n * stay byte-for-byte what they were: this is an addition to the contract, not\n * a change to it, and the term listing's own footer is left alone for the same\n * reason.\n */\nimport type { Command } from \"commander\";\nimport { emitJson, fail } from \"../../core/envelope/json.js\";\nimport { LOAM_VERSION } from \"../../core/envelope/version.js\";\nimport {\n explainSubject,\n firstSentence,\n knownSubjects,\n listCodes,\n listTerms,\n type CodeListing,\n type Explanation,\n} from \"../../core/explain/lookup.js\";\nimport { nearestIds } from \"../../core/repo/entries.js\";\n\ninterface ExplainOptions {\n json?: boolean;\n codes?: boolean;\n}\n\n// Interpolated from the registry that owns the terms, never hand-listed: a\n// copied list beside TERMS is a list that silently trails it.\nconst TERM_LIST = listTerms()\n .map(({ term }) => term)\n .join(\", \");\n\n/** The listing printed when no subject is asked for — also where codes are said to come from. */\nfunction printTermListing(json: boolean): void {\n const terms = listTerms();\n if (json) {\n emitJson({ command: \"explain\", version: LOAM_VERSION, terms: terms.map(({ term, summary }) => ({ term, description: summary })) });\n return;\n }\n console.log(`loam ${LOAM_VERSION} — the vocabulary\\n`);\n const width = Math.max(...terms.map(({ term }) => term.length));\n for (const { term, summary } of terms) console.log(` ${term.padEnd(width)} ${summary}`);\n console.log(\n \"\\nFinding codes come from `loam validate` and `loam verify` output; refusal codes arrive as error.code\" +\n \"\\nin any --json envelope. `loam explain <subject>` explains one.\",\n );\n}\n\n/**\n * How much of a code's meaning the inventory shows before it stops.\n *\n * The term listing prints whole first sentences because there are six of them\n * and each is under 200 characters. The code listing prints 270-odd rows whose\n * first sentences run to 377 characters, and a row that wraps four times is a\n * paragraph, not a line of a table — it destroys the one thing this listing is\n * for, which is scanning names. Nothing is lost: the sentence is verbatim in\n * `--json`, and `loam explain <code>` prints the whole entry.\n */\nconst SUMMARY_WIDTH = 72;\n\n/** The listing's one-line gloss: the first sentence of the meaning, cut at a word boundary if it runs long. */\nfunction summarize(listing: CodeListing): string {\n // A code graded in two tables is glossed by the FIRST table's meaning — the\n // listing answers \"what is this about\", and the per-scope difference (which\n // `explain <code>` prints in full, one block per table) is exactly the\n // detail a one-line row cannot carry honestly.\n const sentence = firstSentence(listing.kind === \"finding\" ? listing.entries[0]!.meaning : listing.meaning);\n if (sentence.length <= SUMMARY_WIDTH) return sentence;\n const cut = sentence.lastIndexOf(\" \", SUMMARY_WIDTH);\n return `${sentence.slice(0, cut === -1 ? SUMMARY_WIDTH : cut)}…`;\n}\n\n/**\n * `--codes`: the whole code vocabulary, the inventory that was previously\n * enumerable only out of TypeScript unions or the 83 KB /loam-check prose.\n * The JSON rows are the per-code answers verbatim (core/explain/lookup.ts\n * derives `CodeListing` from `Explanation`), so a consumer can build a\n * code-to-fix cache from one call instead of one call per code.\n */\nfunction printCodeListing(json: boolean): void {\n const codes = listCodes();\n if (json) {\n emitJson({ command: \"explain\", version: LOAM_VERSION, codes });\n return;\n }\n const findings = codes.filter((listing) => listing.kind === \"finding\");\n const refusals = codes.filter((listing) => listing.kind === \"refusal\");\n const width = Math.max(...codes.map(({ code }) => code.length));\n const print = (group: CodeListing[]): void => {\n for (const listing of group) console.log(` ${listing.code.padEnd(width)} ${summarize(listing)}`);\n };\n console.log(`loam ${LOAM_VERSION} — the code vocabulary (${findings.length} finding, ${refusals.length} refusal)\\n`);\n console.log(\"finding codes — graded by `loam validate` and `loam verify`, in their findings[]\");\n print(findings);\n console.log(\"\\nrefusal codes — the `error.code` of a --json envelope, exit 1\");\n print(refusals);\n console.log(\"\\n`loam explain <code>` prints one in full; `loam explain` with no argument lists the concept terms.\");\n}\n\nfunction printText(subject: string, explanation: Explanation): void {\n if (explanation.kind === \"term\") {\n console.log(`${explanation.term}\\n\\n${explanation.paragraph}`);\n return;\n }\n if (explanation.kind === \"refusal\") {\n console.log(`${subject} — a refusal code (error.code in the --json envelope, exit 1)\\n\\n${explanation.meaning}`);\n return;\n }\n // A finding: one block per table the code appears in, under the table's own\n // column names, so the page reads as the /loam-check row it is. The\n // severity note repeats on each scope block because it is a PER-TABLE fact:\n // `openapi.ref-unresolved` is a warn under `--service` and an error at\n // archive plan time, and a reader asking \"does this block my archive?\" must\n // not have to guess which note belongs to which context.\n const severities = [...new Set(explanation.entries.map((entry) => entry.severityNote))];\n console.log(`${subject} (${severities.join(\"; \")})${explanation.via === undefined ? \"\" : ` — surfaced by \\`${explanation.via}\\``}`);\n for (const entry of explanation.entries) {\n console.log(`\\n[${entry.scope}] (${entry.severityNote})`);\n console.log(` what it means: ${entry.meaning}`);\n if (entry.fix !== \"\") console.log(` what to do: ${entry.fix}`);\n }\n}\n\nexport function registerExplain(program: Command): void {\n program\n .command(\"explain\")\n .argument(\n \"[subject]\",\n `a finding code (spine.op-undefined), a refusal code (docs-busy), or a concept term (${TERM_LIST}); omit to list the terms`,\n )\n .description(\"Explain a finding code, refusal code or concept term, version-matched to this binary\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .option(\"--codes\", \"list every finding and refusal code this binary can explain\")\n .action((subject: string | undefined, opts: ExplainOptions) => {\n const json = opts.json === true;\n if (opts.codes === true) {\n // Refused rather than quietly preferring one: `loam explain\n // spine.op-undefined --codes` asks two different questions, and a\n // caller who typed both wanted whichever one this run did NOT pick.\n if (subject !== undefined) {\n fail(\n json,\n \"invalid-option\",\n `--codes lists the whole vocabulary and takes no subject — drop '${subject}' for the inventory, or drop --codes to explain that one.`,\n );\n return;\n }\n printCodeListing(json);\n return;\n }\n if (subject === undefined) {\n printTermListing(json);\n return;\n }\n const explanation = explainSubject(subject);\n if (explanation === null) {\n // The same code `show` and `instructions` use for a name that resolves\n // to nothing, with `nearestIds` suggestions because the known set here\n // is ~200 strings — a caller one typo away should not have to diff the\n // whole vocabulary to find it.\n const near = nearestIds(subject, knownSubjects());\n fail(\n json,\n \"unknown-target\",\n `Nothing named '${subject}' — not a finding code, a refusal code, or a concept term.` +\n (near.length > 0 ? ` Close: ${near.join(\", \")}.` : \"\") +\n \" Run `loam explain` with no argument for the concept list; finding codes appear in `loam validate` and `loam verify` output.\",\n );\n return;\n }\n if (json) {\n emitJson({\n command: \"explain\",\n version: LOAM_VERSION,\n subject,\n kind: explanation.kind,\n ...(explanation.kind === \"finding\"\n ? { entries: explanation.entries, ...(explanation.via === undefined ? {} : { via: explanation.via }) }\n : explanation.kind === \"refusal\"\n ? { meaning: explanation.meaning }\n : { term: explanation.term, paragraph: explanation.paragraph }),\n });\n return;\n }\n printText(subject, explanation);\n });\n}\n"]}
|
package/dist/commands/explore.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { loadConfig } from "../core/config.js";
|
|
2
|
-
import { explore } from "../core/explore.js";
|
|
1
|
+
import { loadConfig } from "../core/envelope/config.js";
|
|
2
|
+
import { explore } from "../core/explore/explore.js";
|
|
3
3
|
import { FleetContext } from "../core/fleet-context.js";
|
|
4
|
-
import { emitJson, fail, reportNoConfig } from "../core/json.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
4
|
+
import { emitJson, fail, reportNoConfig } from "../core/envelope/json.js";
|
|
5
|
+
import { featureIdProblem } from "../core/kernel/ids/feature.js";
|
|
6
|
+
import { parseServiceIds } from "../core/kernel/ids/service.js";
|
|
7
|
+
import { DocsRepoUnavailableError } from "../core/repo/state.js";
|
|
8
|
+
import { docsRepoReady, reportDocsRepoError, reportRepositoryUnavailable } from "./policy/gate.js";
|
|
9
|
+
import { plural } from "./policy/format.js";
|
|
9
10
|
/**
|
|
10
11
|
* The feature id in the suggested command line when `--as` did not say.
|
|
11
12
|
*
|
|
@@ -28,11 +29,13 @@ export function registerExplore(program) {
|
|
|
28
29
|
.description("Read the fleet around a change nobody has written down yet — writes nothing")
|
|
29
30
|
.option("--json", "emit the machine contract instead of the human view")
|
|
30
31
|
.option("--op <operationId>", "seed from an operation instead of a service; repeatable", (value, prev = []) => [...prev, value])
|
|
32
|
+
.option("--capability <id>", "seed from a declared capability's realizing services; repeatable", (value, prev = []) => [...prev, value])
|
|
31
33
|
.option("--as <FEAT>", `feature id for the suggested command line (default: ${PLACEHOLDER})`)
|
|
32
34
|
.action(async (serviceArgs, opts) => {
|
|
33
35
|
const json = opts.json === true;
|
|
34
36
|
const services = serviceArgs ?? [];
|
|
35
37
|
const operations = opts.op ?? [];
|
|
38
|
+
const capabilities = opts.capability ?? [];
|
|
36
39
|
// `--as` is interpolated into the `loam new` line this command prints
|
|
37
40
|
// under `next:` and returns as `scaffold`, and `loam-feature` teaches an
|
|
38
41
|
// agent to pass `$1` straight into it. Unchecked, `explore` hands back a
|
|
@@ -40,7 +43,7 @@ export function registerExplore(program) {
|
|
|
40
43
|
// something loam rejects, which is the one thing the printed-command
|
|
41
44
|
// guard exists to prevent and the one shape it cannot see, because it
|
|
42
45
|
// reads literal source strings and this line is assembled from argv.
|
|
43
|
-
// Same grammar as `new`'s, from core/ids.ts, so the two cannot disagree.
|
|
46
|
+
// Same grammar as `new`'s, from core/kernel/ids/feature.ts, so the two cannot disagree.
|
|
44
47
|
if (opts.as !== undefined) {
|
|
45
48
|
const problem = featureIdProblem(opts.as, "feature id for --as");
|
|
46
49
|
if (problem !== null) {
|
|
@@ -48,13 +51,14 @@ export function registerExplore(program) {
|
|
|
48
51
|
return;
|
|
49
52
|
}
|
|
50
53
|
}
|
|
51
|
-
if (services.length === 0 && operations.length === 0) {
|
|
52
|
-
fail(json, "invalid-option", "Nothing to explore from. Name at least one service, or pass --op <operationId>. " +
|
|
53
|
-
"`loam list services` is the fleet; `loam show <service>` is one of them
|
|
54
|
+
if (services.length === 0 && operations.length === 0 && capabilities.length === 0) {
|
|
55
|
+
fail(json, "invalid-option", "Nothing to explore from. Name at least one service, or pass --op <operationId> or --capability <id>. " +
|
|
56
|
+
"`loam list services` is the fleet; `loam show <service>` is one of them; " +
|
|
57
|
+
"`loam list capabilities` is what the fleet promises.");
|
|
54
58
|
return;
|
|
55
59
|
}
|
|
56
60
|
// Rule 6 in docs/DESIGN.md: a raw argv string that reaches a path join is
|
|
57
|
-
// guarded at the command boundary. Every id here reaches
|
|
61
|
+
// guarded at the command boundary. Every id here reaches the service path builders.
|
|
58
62
|
// Before `loadConfig`, exactly as `loam new` guards its `--touches` list:
|
|
59
63
|
// the grammar is a fact about the argument, not about the repository, so
|
|
60
64
|
// an unusable id should answer the same whether or not you are standing
|
|
@@ -63,21 +67,17 @@ export function registerExplore(program) {
|
|
|
63
67
|
// A seed naming a service that does not EXIST is fine and reported as
|
|
64
68
|
// `unknown` — the feature may be introducing it — but one that could not
|
|
65
69
|
// be a service directory at all is a typo, not an intention.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
catch (err) {
|
|
71
|
-
if (!(err instanceof InvalidIdError))
|
|
72
|
-
throw err;
|
|
73
|
-
fail(json, "invalid-option", err.message);
|
|
70
|
+
const seeds = parseServiceIds(services, "service");
|
|
71
|
+
if (!seeds.ok) {
|
|
72
|
+
fail(json, "invalid-option", seeds.problem);
|
|
74
73
|
return;
|
|
75
74
|
}
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
78
|
-
reportNoConfig(json);
|
|
75
|
+
const loaded = await loadConfig();
|
|
76
|
+
if (loaded.kind !== "loaded") {
|
|
77
|
+
reportNoConfig(json, loaded);
|
|
79
78
|
return;
|
|
80
79
|
}
|
|
80
|
+
const config = loaded.config;
|
|
81
81
|
const { docsDir } = config;
|
|
82
82
|
// `services`, not `docs`: every answer here is a statement about the
|
|
83
83
|
// fleet, and an empty ring read out of a directory that is not a docs
|
|
@@ -87,8 +87,9 @@ export function registerExplore(program) {
|
|
|
87
87
|
try {
|
|
88
88
|
const result = await explore({
|
|
89
89
|
docsDir,
|
|
90
|
-
services,
|
|
90
|
+
services: seeds.ids,
|
|
91
91
|
operations,
|
|
92
|
+
capabilities,
|
|
92
93
|
featureId: opts.as ?? PLACEHOLDER,
|
|
93
94
|
context: new FleetContext(),
|
|
94
95
|
});
|
|
@@ -164,6 +165,10 @@ function print(r) {
|
|
|
164
165
|
console.log(`! no living contract defines ${r.unresolvedOperations.join(", ")} — ` +
|
|
165
166
|
`the feature is adding ${r.unresolvedOperations.length === 1 ? "it" : "them"}, or the id is wrong\n`);
|
|
166
167
|
}
|
|
168
|
+
if (r.unresolvedCapabilities.length > 0) {
|
|
169
|
+
console.log(`! no realizing service seeds ${r.unresolvedCapabilities.join(", ")} — ` +
|
|
170
|
+
`not declared in architecture/capabilities.yaml, or declared and realized by nothing\n`);
|
|
171
|
+
}
|
|
167
172
|
if (r.neighbours.length > 0) {
|
|
168
173
|
// The whole point of the command, so it is stated as a question rather than
|
|
169
174
|
// a list: these are candidates for --touches, and loam does not know which
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explore.js","sourceRoot":"","sources":["../../src/commands/explore.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig } from \"../core/config.js\";\nimport { explore, type Exploration, type ExploreService } from \"../core/explore.js\";\nimport { FleetContext } from \"../core/fleet-context.js\";\nimport { emitJson, fail, reportNoConfig } from \"../core/json.js\";\nimport { assertServiceId, featureIdProblem, InvalidIdError } from \"../core/ids.js\";\nimport { DocsRepoUnavailableError } from \"../core/repo.js\";\nimport { docsRepoReady, reportDocsRepoError, reportRepositoryUnavailable } from \"./docs-repo-gate.js\";\nimport { plural } from \"./format.js\";\n\n/**\n * The feature id in the suggested command line when `--as` did not say.\n *\n * It has to satisfy the feature-id grammar, because `scaffold` is documented as\n * the literal `loam new` line and AGENTS.md's rule is that an instruction loam\n * prints and loam refuses is a defect. `FEAT-NNN` — the obvious placeholder —\n * is exactly that: `loam new FEAT-NNN` comes back `invalid-option`, so the last\n * line of every default run was a command that could not be run.\n *\n * `FEAT-000` parses and is still unmistakably a placeholder, which buys the\n * property the refusal was standing in for: an agent that pastes it unedited\n * gets a feature called FEAT-000, visible in `loam list` within seconds and\n * deletable with `git rm -r`. A plausible id (FEAT-1) would not be.\n */\nconst PLACEHOLDER = \"FEAT-000\";\n\ninterface ExploreOptions {\n json?: boolean;\n op?: string[];\n as?: string;\n}\n\nexport function registerExplore(program: Command): void {\n program\n .command(\"explore\")\n .argument(\"[service...]\", \"service ids the change starts from\")\n .description(\"Read the fleet around a change nobody has written down yet — writes nothing\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .option(\n \"--op <operationId>\",\n \"seed from an operation instead of a service; repeatable\",\n (value: string, prev: string[] = []) => [...prev, value],\n )\n .option(\"--as <FEAT>\", `feature id for the suggested command line (default: ${PLACEHOLDER})`)\n .action(async (serviceArgs: string[], opts: ExploreOptions) => {\n const json = opts.json === true;\n const services = serviceArgs ?? [];\n const operations = opts.op ?? [];\n\n // `--as` is interpolated into the `loam new` line this command prints\n // under `next:` and returns as `scaffold`, and `loam-feature` teaches an\n // agent to pass `$1` straight into it. Unchecked, `explore` hands back a\n // command `loam new` then refuses — loam instructing an agent to run\n // something loam rejects, which is the one thing the printed-command\n // guard exists to prevent and the one shape it cannot see, because it\n // reads literal source strings and this line is assembled from argv.\n // Same grammar as `new`'s, from core/ids.ts, so the two cannot disagree.\n if (opts.as !== undefined) {\n const problem = featureIdProblem(opts.as, \"feature id for --as\");\n if (problem !== null) {\n fail(json, \"invalid-option\", problem);\n return;\n }\n }\n if (services.length === 0 && operations.length === 0) {\n fail(\n json,\n \"invalid-option\",\n \"Nothing to explore from. Name at least one service, or pass --op <operationId>. \" +\n \"`loam list services` is the fleet; `loam show <service>` is one of them.\",\n );\n return;\n }\n\n // Rule 6 in docs/DESIGN.md: a raw argv string that reaches a path join is\n // guarded at the command boundary. Every id here reaches `servicePaths`.\n // Before `loadConfig`, exactly as `loam new` guards its `--touches` list:\n // the grammar is a fact about the argument, not about the repository, so\n // an unusable id should answer the same whether or not you are standing\n // in a wired repo.\n //\n // A seed naming a service that does not EXIST is fine and reported as\n // `unknown` — the feature may be introducing it — but one that could not\n // be a service directory at all is a typo, not an intention.\n try {\n for (const id of services) assertServiceId(id, \"service\");\n } catch (err) {\n if (!(err instanceof InvalidIdError)) throw err;\n fail(json, \"invalid-option\", err.message);\n return;\n }\n\n const config = await loadConfig();\n if (!config) {\n reportNoConfig(json);\n return;\n }\n const { docsDir } = config;\n // `services`, not `docs`: every answer here is a statement about the\n // fleet, and an empty ring read out of a directory that is not a docs\n // repo says \"nothing calls this service\" about a repo nobody looked in.\n if (!docsRepoReady(json, docsDir, \"services\")) return;\n\n try {\n const result = await explore({\n docsDir,\n services,\n operations,\n featureId: opts.as ?? PLACEHOLDER,\n context: new FleetContext(),\n });\n if (json) {\n emitJson({ command: \"explore\", docsDir, ...result });\n return;\n }\n print(result);\n } catch (err) {\n if (err instanceof DocsRepoUnavailableError) {\n reportDocsRepoError(json, err);\n return;\n }\n // The scan is all-or-nothing — it reads every seed's contract to build\n // one table — so the honest answer to an unreadable file is a refusal\n // naming the path, not a ring with a silent hole in it.\n reportRepositoryUnavailable(json, err, \"the exploration would be missing a service\", docsDir);\n }\n });\n}\n\n/* ------------------------------------------------------------------ */\n\nfunction print(r: Exploration): void {\n if (!r.landscape.present || !r.landscape.parses) {\n // Said first and without hedging: with no readable map there IS no ring,\n // and a reader who takes the empty neighbour list at face value concludes\n // the change is contained when nothing checked that.\n const why = r.landscape.present ? \"does not parse\" : \"does not exist\";\n console.log(\n `! architecture/landscape.likec4 ${why} — no neighbour below is derived, and none is missing either\\n`,\n );\n }\n\n for (const s of r.services) {\n console.log(`${s.id} ${label(s)}`);\n if (!s.known) {\n // Two different facts wear the same absence. A SEED nobody typed by\n // accident is a service the feature introduces; one typed by accident is\n // a typo, and the near-miss list is what separates them. A neighbour is\n // neither — the fleet map drew an edge to something with no\n // `services/<id>/`, which is usually an external system and occasionally\n // a service nobody has adopted. Saying \"typo\" about that one sends a\n // reader looking for a mistake they did not make.\n const near = r.unknown.find((u) => u.id === s.id)?.nearest ?? [];\n const why =\n s.reason === \"seed\"\n ? \"the feature introduces it, or the id is a typo\"\n : \"an external system, or a service nobody has adopted\";\n console.log(\n ` no services/${s.id}/ — ${why}` +\n (near.length > 0 ? `; closest existing: ${near.join(\", \")}` : \"\"),\n );\n } else {\n if (s.missing.length > 0) console.log(` missing: ${s.missing.join(\", \")}`);\n if (!s.modelled) console.log(` not in the fleet map — no edge into it can be checked`);\n // Said before `exposes`, and said at all: an unreadable contract exposes\n // an empty list, and printing nothing there reads as \"this service offers\n // no endpoints\" — the answer that turns a MODIFIED requirement into an\n // ADDED one.\n if (s.openapi.unreadable) {\n console.log(` ! openapi.yaml does not parse — nothing here lists what ${s.id} exposes`);\n if (s.openapi.error !== undefined) console.log(` ${s.openapi.error}`);\n } else if (s.operations.length > 0) {\n console.log(` exposes: ${s.operations.join(\", \")}`);\n }\n }\n for (const e of s.inbound) console.log(` ← ${e.service}${edgeNote(e.op, e.title)}`);\n for (const e of s.outbound) console.log(` → ${e.service}${edgeNote(e.op, e.title)}`);\n console.log(\"\");\n }\n\n if (r.unresolvedOperations.length > 0) {\n console.log(\n `! no living contract defines ${r.unresolvedOperations.join(\", \")} — ` +\n `the feature is adding ${r.unresolvedOperations.length === 1 ? \"it\" : \"them\"}, or the id is wrong\\n`,\n );\n }\n\n if (r.neighbours.length > 0) {\n // The whole point of the command, so it is stated as a question rather than\n // a list: these are candidates for --touches, and loam does not know which\n // of them this change actually touches.\n console.log(\n `${plural(r.neighbours.length, \"neighbour\")} one hop out: ${r.neighbours.join(\", \")}`,\n );\n console.log(\" weigh each one — loam knows they are connected, not whether you change them\\n\");\n }\n\n if (r.overlaps.length > 0) {\n console.log(\"already in flight over this ground\");\n for (const o of r.overlaps) console.log(` ${o.feature} ${o.services.join(\", \")}`);\n console.log(\" `loam dependencies` says which must archive first, and what no ordering fixes\\n\");\n }\n\n console.log(`next: ${r.scaffold}`);\n}\n\nfunction label(s: ExploreService): string {\n const where = s.reason === \"seed\" ? \"seed\" : s.reason === \"calls-seed\" ? \"calls a seed\" : \"called by a seed\";\n // `known`, matching the branch in `print` — not `maturity === null`. The two\n // are set together and cannot disagree today, but discriminating on different\n // fields for the same question is how they start to: a reader fixing one\n // branch has no reason to look at the other.\n return s.known ? `${where} · ${s.maturity}` : `${where} · not in the docs repo`;\n}\n\nfunction edgeNote(op: string | null, title: string | null): string {\n if (op !== null) return ` ${op}`;\n return title !== null ? ` \"${title}\"` : \"\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"explore.js","sourceRoot":"","sources":["../../src/commands/explore.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig } from \"../core/envelope/config.js\";\nimport { explore, type Exploration, type ExploreService } from \"../core/explore/explore.js\";\nimport { FleetContext } from \"../core/fleet-context.js\";\nimport { emitJson, fail, reportNoConfig } from \"../core/envelope/json.js\";\nimport { featureIdProblem } from \"../core/kernel/ids/feature.js\";\nimport { parseServiceIds } from \"../core/kernel/ids/service.js\";\nimport { DocsRepoUnavailableError } from \"../core/repo/state.js\";\nimport { docsRepoReady, reportDocsRepoError, reportRepositoryUnavailable } from \"./policy/gate.js\";\nimport { plural } from \"./policy/format.js\";\n\n/**\n * The feature id in the suggested command line when `--as` did not say.\n *\n * It has to satisfy the feature-id grammar, because `scaffold` is documented as\n * the literal `loam new` line and AGENTS.md's rule is that an instruction loam\n * prints and loam refuses is a defect. `FEAT-NNN` — the obvious placeholder —\n * is exactly that: `loam new FEAT-NNN` comes back `invalid-option`, so the last\n * line of every default run was a command that could not be run.\n *\n * `FEAT-000` parses and is still unmistakably a placeholder, which buys the\n * property the refusal was standing in for: an agent that pastes it unedited\n * gets a feature called FEAT-000, visible in `loam list` within seconds and\n * deletable with `git rm -r`. A plausible id (FEAT-1) would not be.\n */\nconst PLACEHOLDER = \"FEAT-000\";\n\ninterface ExploreOptions {\n json?: boolean;\n op?: string[];\n capability?: string[];\n as?: string;\n}\n\nexport function registerExplore(program: Command): void {\n program\n .command(\"explore\")\n .argument(\"[service...]\", \"service ids the change starts from\")\n .description(\"Read the fleet around a change nobody has written down yet — writes nothing\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .option(\n \"--op <operationId>\",\n \"seed from an operation instead of a service; repeatable\",\n (value: string, prev: string[] = []) => [...prev, value],\n )\n .option(\n \"--capability <id>\",\n \"seed from a declared capability's realizing services; repeatable\",\n (value: string, prev: string[] = []) => [...prev, value],\n )\n .option(\"--as <FEAT>\", `feature id for the suggested command line (default: ${PLACEHOLDER})`)\n .action(async (serviceArgs: string[], opts: ExploreOptions) => {\n const json = opts.json === true;\n const services = serviceArgs ?? [];\n const operations = opts.op ?? [];\n const capabilities = opts.capability ?? [];\n\n // `--as` is interpolated into the `loam new` line this command prints\n // under `next:` and returns as `scaffold`, and `loam-feature` teaches an\n // agent to pass `$1` straight into it. Unchecked, `explore` hands back a\n // command `loam new` then refuses — loam instructing an agent to run\n // something loam rejects, which is the one thing the printed-command\n // guard exists to prevent and the one shape it cannot see, because it\n // reads literal source strings and this line is assembled from argv.\n // Same grammar as `new`'s, from core/kernel/ids/feature.ts, so the two cannot disagree.\n if (opts.as !== undefined) {\n const problem = featureIdProblem(opts.as, \"feature id for --as\");\n if (problem !== null) {\n fail(json, \"invalid-option\", problem);\n return;\n }\n }\n if (services.length === 0 && operations.length === 0 && capabilities.length === 0) {\n fail(\n json,\n \"invalid-option\",\n \"Nothing to explore from. Name at least one service, or pass --op <operationId> or --capability <id>. \" +\n \"`loam list services` is the fleet; `loam show <service>` is one of them; \" +\n \"`loam list capabilities` is what the fleet promises.\",\n );\n return;\n }\n\n // Rule 6 in docs/DESIGN.md: a raw argv string that reaches a path join is\n // guarded at the command boundary. Every id here reaches the service path builders.\n // Before `loadConfig`, exactly as `loam new` guards its `--touches` list:\n // the grammar is a fact about the argument, not about the repository, so\n // an unusable id should answer the same whether or not you are standing\n // in a wired repo.\n //\n // A seed naming a service that does not EXIST is fine and reported as\n // `unknown` — the feature may be introducing it — but one that could not\n // be a service directory at all is a typo, not an intention.\n const seeds = parseServiceIds(services, \"service\");\n if (!seeds.ok) {\n fail(json, \"invalid-option\", seeds.problem);\n return;\n }\n\n const loaded = await loadConfig();\n if (loaded.kind !== \"loaded\") {\n reportNoConfig(json, loaded);\n return;\n }\n const config = loaded.config;\n const { docsDir } = config;\n // `services`, not `docs`: every answer here is a statement about the\n // fleet, and an empty ring read out of a directory that is not a docs\n // repo says \"nothing calls this service\" about a repo nobody looked in.\n if (!docsRepoReady(json, docsDir, \"services\")) return;\n\n try {\n const result = await explore({\n docsDir,\n services: seeds.ids,\n operations,\n capabilities,\n featureId: opts.as ?? PLACEHOLDER,\n context: new FleetContext(),\n });\n if (json) {\n emitJson({ command: \"explore\", docsDir, ...result });\n return;\n }\n print(result);\n } catch (err) {\n if (err instanceof DocsRepoUnavailableError) {\n reportDocsRepoError(json, err);\n return;\n }\n // The scan is all-or-nothing — it reads every seed's contract to build\n // one table — so the honest answer to an unreadable file is a refusal\n // naming the path, not a ring with a silent hole in it.\n reportRepositoryUnavailable(json, err, \"the exploration would be missing a service\", docsDir);\n }\n });\n}\n\n/* ------------------------------------------------------------------ */\n\nfunction print(r: Exploration): void {\n if (!r.landscape.present || !r.landscape.parses) {\n // Said first and without hedging: with no readable map there IS no ring,\n // and a reader who takes the empty neighbour list at face value concludes\n // the change is contained when nothing checked that.\n const why = r.landscape.present ? \"does not parse\" : \"does not exist\";\n console.log(\n `! architecture/landscape.likec4 ${why} — no neighbour below is derived, and none is missing either\\n`,\n );\n }\n\n for (const s of r.services) {\n console.log(`${s.id} ${label(s)}`);\n if (!s.known) {\n // Two different facts wear the same absence. A SEED nobody typed by\n // accident is a service the feature introduces; one typed by accident is\n // a typo, and the near-miss list is what separates them. A neighbour is\n // neither — the fleet map drew an edge to something with no\n // `services/<id>/`, which is usually an external system and occasionally\n // a service nobody has adopted. Saying \"typo\" about that one sends a\n // reader looking for a mistake they did not make.\n const near = r.unknown.find((u) => u.id === s.id)?.nearest ?? [];\n const why =\n s.reason === \"seed\"\n ? \"the feature introduces it, or the id is a typo\"\n : \"an external system, or a service nobody has adopted\";\n console.log(\n ` no services/${s.id}/ — ${why}` +\n (near.length > 0 ? `; closest existing: ${near.join(\", \")}` : \"\"),\n );\n } else {\n if (s.missing.length > 0) console.log(` missing: ${s.missing.join(\", \")}`);\n if (!s.modelled) console.log(` not in the fleet map — no edge into it can be checked`);\n // Said before `exposes`, and said at all: an unreadable contract exposes\n // an empty list, and printing nothing there reads as \"this service offers\n // no endpoints\" — the answer that turns a MODIFIED requirement into an\n // ADDED one.\n if (s.openapi.unreadable) {\n console.log(` ! openapi.yaml does not parse — nothing here lists what ${s.id} exposes`);\n if (s.openapi.error !== undefined) console.log(` ${s.openapi.error}`);\n } else if (s.operations.length > 0) {\n console.log(` exposes: ${s.operations.join(\", \")}`);\n }\n }\n for (const e of s.inbound) console.log(` ← ${e.service}${edgeNote(e.op, e.title)}`);\n for (const e of s.outbound) console.log(` → ${e.service}${edgeNote(e.op, e.title)}`);\n console.log(\"\");\n }\n\n if (r.unresolvedOperations.length > 0) {\n console.log(\n `! no living contract defines ${r.unresolvedOperations.join(\", \")} — ` +\n `the feature is adding ${r.unresolvedOperations.length === 1 ? \"it\" : \"them\"}, or the id is wrong\\n`,\n );\n }\n\n if (r.unresolvedCapabilities.length > 0) {\n console.log(\n `! no realizing service seeds ${r.unresolvedCapabilities.join(\", \")} — ` +\n `not declared in architecture/capabilities.yaml, or declared and realized by nothing\\n`,\n );\n }\n\n if (r.neighbours.length > 0) {\n // The whole point of the command, so it is stated as a question rather than\n // a list: these are candidates for --touches, and loam does not know which\n // of them this change actually touches.\n console.log(\n `${plural(r.neighbours.length, \"neighbour\")} one hop out: ${r.neighbours.join(\", \")}`,\n );\n console.log(\" weigh each one — loam knows they are connected, not whether you change them\\n\");\n }\n\n if (r.overlaps.length > 0) {\n console.log(\"already in flight over this ground\");\n for (const o of r.overlaps) console.log(` ${o.feature} ${o.services.join(\", \")}`);\n console.log(\" `loam dependencies` says which must archive first, and what no ordering fixes\\n\");\n }\n\n console.log(`next: ${r.scaffold}`);\n}\n\nfunction label(s: ExploreService): string {\n const where = s.reason === \"seed\" ? \"seed\" : s.reason === \"calls-seed\" ? \"calls a seed\" : \"called by a seed\";\n // `known`, matching the branch in `print` — not `maturity === null`. The two\n // are set together and cannot disagree today, but discriminating on different\n // fields for the same question is how they start to: a reader fixing one\n // branch has no reason to look at the other.\n return s.known ? `${where} · ${s.maturity}` : `${where} · not in the docs repo`;\n}\n\nfunction edgeNote(op: string | null, title: string | null): string {\n if (op !== null) return ` ${op}`;\n return title !== null ? ` \"${title}\"` : \"\";\n}\n"]}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { loadConfig } from "../../core/envelope/config.js";
|
|
2
|
+
import { emitJson, fail, NO_SERVICE_MESSAGE, reportNoConfig } from "../../core/envelope/json.js";
|
|
3
|
+
import { FleetContext } from "../../core/fleet-context.js";
|
|
4
|
+
import { gateReport } from "../../core/gate/checks.js";
|
|
5
|
+
import { gateSummary, gateVerdict } from "../../core/gate/report.js";
|
|
6
|
+
import { nearestIds } from "../../core/repo/entries.js";
|
|
7
|
+
import { enumeratedServiceIds, resolveServiceTarget } from "../../core/repo/service-target.js";
|
|
8
|
+
import { DocsRepoUnavailableError } from "../../core/repo/state.js";
|
|
9
|
+
import { findingJson } from "../../core/vocabulary/report.js";
|
|
10
|
+
import { docsRepoReady, reportDocsRepoError, reportRepositoryUnavailable, } from "../policy/gate.js";
|
|
11
|
+
import { printGate } from "./print.js";
|
|
12
|
+
export function registerGate(program) {
|
|
13
|
+
program
|
|
14
|
+
.command("gate")
|
|
15
|
+
.description("Can this service deploy? A read-only query over recorded evidence — advisory by default")
|
|
16
|
+
.option("--service <id>", "the service being deployed (defaults to the configured service)")
|
|
17
|
+
.option("--strict", "exit 1 on any warning too — a per-invocation CI lever; the report and --json payload do not change")
|
|
18
|
+
.option("--json", "emit the machine contract instead of the human view")
|
|
19
|
+
.action(async (opts) => {
|
|
20
|
+
const json = opts.json === true;
|
|
21
|
+
const loaded = await loadConfig();
|
|
22
|
+
if (loaded.kind !== "loaded") {
|
|
23
|
+
reportNoConfig(json, loaded);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const config = loaded.config;
|
|
27
|
+
const { docsDir } = config;
|
|
28
|
+
// The gate joins partners and enumerates features against services/ —
|
|
29
|
+
// a docsDir that is not a docs repo must refuse, never answer "pass"
|
|
30
|
+
// over an empty fleet (docsRepoReady's whole doctrine).
|
|
31
|
+
if (!docsRepoReady(json, docsDir, "services"))
|
|
32
|
+
return;
|
|
33
|
+
// The deploy-pipeline invocation is literally `loam gate --json` in a
|
|
34
|
+
// bound service repo: the binding IS the service being deployed.
|
|
35
|
+
const service = opts.service ?? config.service;
|
|
36
|
+
if (!service) {
|
|
37
|
+
fail(json, "invalid-option", NO_SERVICE_MESSAGE);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// One invocation, one filesystem snapshot (validate's rule).
|
|
41
|
+
const fleet = new FleetContext();
|
|
42
|
+
try {
|
|
43
|
+
// Enumeration first, grammar second — service-target.ts's banner: the
|
|
44
|
+
// one directory a caller most needs to ask about may carry a name the
|
|
45
|
+
// grammar refuses, so the raw argument is never assertServiceId'd here.
|
|
46
|
+
const resolved = await resolveServiceTarget(docsDir, service, "--service", fleet);
|
|
47
|
+
if (!resolved.ok) {
|
|
48
|
+
fail(json, "invalid-option", resolved.problem);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (resolved.dir === undefined) {
|
|
52
|
+
// Grammar-legal but unenumerated: a question recorded evidence
|
|
53
|
+
// cannot answer — Pact's gate answers unknown participants with a
|
|
54
|
+
// refusal, not a pass (status's `unknown-service` precedent).
|
|
55
|
+
const near = nearestIds(resolved.id, await enumeratedServiceIds(docsDir, fleet));
|
|
56
|
+
fail(json, "unknown-service", `No service '${resolved.id}' under ${docsDir}/services/ — nothing is recorded for a service nobody adopted, so the deploy question is unanswerable.` +
|
|
57
|
+
(near.length > 0 ? ` Did you mean: ${near.join(", ")}?` : "") +
|
|
58
|
+
` Run \`loam adopt --service ${resolved.id}\` to write its baseline first.`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// `sources` only mean something from inside the service's own
|
|
62
|
+
// repository — validate's repoOf rule. `config.root` rather than
|
|
63
|
+
// process.cwd(): the config's own directory is the repo root, and a
|
|
64
|
+
// run from src/deep/ must grade the same paths as one from the root.
|
|
65
|
+
const bound = config.service;
|
|
66
|
+
const report = await gateReport({
|
|
67
|
+
docsDir,
|
|
68
|
+
service: resolved.id,
|
|
69
|
+
repoDir: bound === resolved.id ? (config.root ?? process.cwd()) : undefined,
|
|
70
|
+
fleet,
|
|
71
|
+
});
|
|
72
|
+
const verdict = gateVerdict(report.checks);
|
|
73
|
+
const summary = gateSummary(report.checks);
|
|
74
|
+
if (json) {
|
|
75
|
+
emitJson({
|
|
76
|
+
command: "gate",
|
|
77
|
+
docsDir,
|
|
78
|
+
service: report.service,
|
|
79
|
+
verdict,
|
|
80
|
+
strict: opts.strict === true,
|
|
81
|
+
landscape: report.landscape,
|
|
82
|
+
partners: report.partners,
|
|
83
|
+
features: report.features,
|
|
84
|
+
checks: report.checks.map((c) => ({
|
|
85
|
+
check: c.check,
|
|
86
|
+
findings: c.findings.map((finding) => findingJson(finding)),
|
|
87
|
+
})),
|
|
88
|
+
summary,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
printGate(report, verdict, summary);
|
|
93
|
+
}
|
|
94
|
+
// --strict moves only the exit code, never the verdict or the payload
|
|
95
|
+
// — validate's exact stance, documented on its own strict block: two
|
|
96
|
+
// pipelines reading one repo may grade the same report differently
|
|
97
|
+
// and both are telling the truth.
|
|
98
|
+
if (verdict === "fail" || (opts.strict === true && summary.warnings > 0)) {
|
|
99
|
+
process.exitCode = 1;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
// The docs repo went away between the gate above and the reads.
|
|
104
|
+
if (err instanceof DocsRepoUnavailableError) {
|
|
105
|
+
reportDocsRepoError(json, err);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// An IO failure the per-subject containment could not localise — it
|
|
109
|
+
// happened in an enumeration, so no partial report exists to give.
|
|
110
|
+
// The recognizer throws anything carrying no errno untouched.
|
|
111
|
+
reportRepositoryUnavailable(json, err, `the deploy question for '${service}' is unanswerable`, docsDir);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../../src/commands/gate/gate.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `loam gate` — can this service deploy, as far as RECORDED evidence can say?\n *\n * A pure query for DEPLOY pipelines outside loam's own lifecycle (Pact's\n * can-i-deploy insight: the gate executes nothing and queries evidence\n * previously recorded). It reads, never writes, never takes the docs lock, and\n * deliberately changes NOTHING about what gates `loam archive` — verify still\n * never gates the merge (WORKFLOW.md, \"What actually gates\"); this command's\n * verdict is advice to a pipeline loam does not own. Advisory by default:\n * warnings exit 0, errors exit 1, and `--strict` is the CI lever that\n * escalates warnings — validate's exact idiom, not a second spelling of it.\n *\n * Not to be confused with `commands/policy/gate.ts`, which is the docs-repo\n * READINESS doctrine (docsRepoReady and friends) every reading command shares\n * — including this one, one import below.\n */\nimport type { Command } from \"commander\";\nimport { loadConfig } from \"../../core/envelope/config.js\";\nimport { emitJson, fail, NO_SERVICE_MESSAGE, reportNoConfig } from \"../../core/envelope/json.js\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport { gateReport } from \"../../core/gate/checks.js\";\nimport { gateSummary, gateVerdict } from \"../../core/gate/report.js\";\nimport { nearestIds } from \"../../core/repo/entries.js\";\nimport { enumeratedServiceIds, resolveServiceTarget } from \"../../core/repo/service-target.js\";\nimport { DocsRepoUnavailableError } from \"../../core/repo/state.js\";\nimport { findingJson } from \"../../core/vocabulary/report.js\";\nimport {\n docsRepoReady,\n reportDocsRepoError,\n reportRepositoryUnavailable,\n} from \"../policy/gate.js\";\nimport { printGate } from \"./print.js\";\n\ninterface GateOptions {\n service?: string;\n strict?: boolean;\n json?: boolean;\n}\n\nexport function registerGate(program: Command): void {\n program\n .command(\"gate\")\n .description(\"Can this service deploy? A read-only query over recorded evidence — advisory by default\")\n .option(\"--service <id>\", \"the service being deployed (defaults to the configured service)\")\n .option(\"--strict\", \"exit 1 on any warning too — a per-invocation CI lever; the report and --json payload do not change\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (opts: GateOptions) => {\n const json = opts.json === true;\n const loaded = await loadConfig();\n if (loaded.kind !== \"loaded\") {\n reportNoConfig(json, loaded);\n return;\n }\n const config = loaded.config;\n const { docsDir } = config;\n // The gate joins partners and enumerates features against services/ —\n // a docsDir that is not a docs repo must refuse, never answer \"pass\"\n // over an empty fleet (docsRepoReady's whole doctrine).\n if (!docsRepoReady(json, docsDir, \"services\")) return;\n // The deploy-pipeline invocation is literally `loam gate --json` in a\n // bound service repo: the binding IS the service being deployed.\n const service = opts.service ?? config.service;\n if (!service) {\n fail(json, \"invalid-option\", NO_SERVICE_MESSAGE);\n return;\n }\n // One invocation, one filesystem snapshot (validate's rule).\n const fleet = new FleetContext();\n\n try {\n // Enumeration first, grammar second — service-target.ts's banner: the\n // one directory a caller most needs to ask about may carry a name the\n // grammar refuses, so the raw argument is never assertServiceId'd here.\n const resolved = await resolveServiceTarget(docsDir, service, \"--service\", fleet);\n if (!resolved.ok) {\n fail(json, \"invalid-option\", resolved.problem);\n return;\n }\n if (resolved.dir === undefined) {\n // Grammar-legal but unenumerated: a question recorded evidence\n // cannot answer — Pact's gate answers unknown participants with a\n // refusal, not a pass (status's `unknown-service` precedent).\n const near = nearestIds(resolved.id, await enumeratedServiceIds(docsDir, fleet));\n fail(\n json,\n \"unknown-service\",\n `No service '${resolved.id}' under ${docsDir}/services/ — nothing is recorded for a service nobody adopted, so the deploy question is unanswerable.` +\n (near.length > 0 ? ` Did you mean: ${near.join(\", \")}?` : \"\") +\n ` Run \\`loam adopt --service ${resolved.id}\\` to write its baseline first.`,\n );\n return;\n }\n // `sources` only mean something from inside the service's own\n // repository — validate's repoOf rule. `config.root` rather than\n // process.cwd(): the config's own directory is the repo root, and a\n // run from src/deep/ must grade the same paths as one from the root.\n const bound: string | undefined = config.service;\n const report = await gateReport({\n docsDir,\n service: resolved.id,\n repoDir: bound === resolved.id ? (config.root ?? process.cwd()) : undefined,\n fleet,\n });\n const verdict = gateVerdict(report.checks);\n const summary = gateSummary(report.checks);\n if (json) {\n emitJson({\n command: \"gate\",\n docsDir,\n service: report.service,\n verdict,\n strict: opts.strict === true,\n landscape: report.landscape,\n partners: report.partners,\n features: report.features,\n checks: report.checks.map((c) => ({\n check: c.check,\n findings: c.findings.map((finding) => findingJson(finding)),\n })),\n summary,\n });\n } else {\n printGate(report, verdict, summary);\n }\n // --strict moves only the exit code, never the verdict or the payload\n // — validate's exact stance, documented on its own strict block: two\n // pipelines reading one repo may grade the same report differently\n // and both are telling the truth.\n if (verdict === \"fail\" || (opts.strict === true && summary.warnings > 0)) {\n process.exitCode = 1;\n }\n } catch (err) {\n // The docs repo went away between the gate above and the reads.\n if (err instanceof DocsRepoUnavailableError) {\n reportDocsRepoError(json, err);\n return;\n }\n // An IO failure the per-subject containment could not localise — it\n // happened in an enumeration, so no partial report exists to give.\n // The recognizer throws anything carrying no errno untouched.\n reportRepositoryUnavailable(\n json,\n err,\n `the deploy question for '${service}' is unanswerable`,\n docsDir,\n );\n }\n });\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The human rendering of a gate report: one block per check, the partner and
|
|
3
|
+
* feature tables as data lines, findings with the shared severity glyphs, and
|
|
4
|
+
* the verdict last — the line a person scanning a deploy log reads first from
|
|
5
|
+
* the bottom. The JSON contract never comes through here; `gate.ts` emits the
|
|
6
|
+
* envelope from the same report, which is what keeps the two views one
|
|
7
|
+
* derivation.
|
|
8
|
+
*/
|
|
9
|
+
import { SEVERITY_MARK } from "../../core/vocabulary/report.js";
|
|
10
|
+
import { plural } from "../policy/format.js";
|
|
11
|
+
function printFindings(findings) {
|
|
12
|
+
for (const f of findings) {
|
|
13
|
+
console.log(` ${SEVERITY_MARK[f.severity]} ${f.message}`);
|
|
14
|
+
for (const d of f.details ?? [])
|
|
15
|
+
console.log(` - ${d}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function printGate(report, verdict, summary) {
|
|
19
|
+
console.log(`gate — ${report.service} (a query over recorded evidence; it executes nothing)`);
|
|
20
|
+
const check = (name) => report.checks.find((c) => c.check === name)?.findings ?? [];
|
|
21
|
+
console.log(`\npartners — landscape ${report.landscape}`);
|
|
22
|
+
for (const p of report.partners) {
|
|
23
|
+
const rung = p.external ? "external" : (p.maturity ?? "no services/ directory");
|
|
24
|
+
console.log(` ${p.service} · ${rung} · ${p.role} · ${p.via.join(", ")}`);
|
|
25
|
+
}
|
|
26
|
+
if (report.partners.length === 0 && report.landscape === "read") {
|
|
27
|
+
console.log(" (the landscape draws no join into or out of this service)");
|
|
28
|
+
}
|
|
29
|
+
printFindings(check("partners"));
|
|
30
|
+
console.log("\nfreshness");
|
|
31
|
+
const freshness = check("freshness");
|
|
32
|
+
if (freshness.length === 0)
|
|
33
|
+
console.log(` ${SEVERITY_MARK.ok} nothing recorded has gone stale`);
|
|
34
|
+
printFindings(freshness);
|
|
35
|
+
console.log("\nverification");
|
|
36
|
+
for (const f of report.features) {
|
|
37
|
+
const attested = f.attested > 0 ? `, ${f.attested} on an agent's word` : "";
|
|
38
|
+
console.log(` ${f.id} · ${f.verdict} (${f.state}; ${f.confirmed}/${f.claims} confirmed${attested})`);
|
|
39
|
+
}
|
|
40
|
+
if (report.features.length === 0) {
|
|
41
|
+
console.log(" (no active feature carries a delta for this service)");
|
|
42
|
+
}
|
|
43
|
+
printFindings(check("verification"));
|
|
44
|
+
console.log("\ninterrupted");
|
|
45
|
+
const interrupted = check("interrupted");
|
|
46
|
+
if (interrupted.length === 0)
|
|
47
|
+
console.log(` ${SEVERITY_MARK.ok} no interrupted commit in the docs repo`);
|
|
48
|
+
printFindings(interrupted);
|
|
49
|
+
console.log(`\nverdict: ${verdict} — ${plural(summary.errors, "error")}, ${plural(summary.warnings, "warning")}` +
|
|
50
|
+
(verdict === "pass" && summary.warnings > 0
|
|
51
|
+
? " (advisory — `--strict` turns warnings into exit 1)"
|
|
52
|
+
: ""));
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=print.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"print.js","sourceRoot":"","sources":["../../../src/commands/gate/print.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The human rendering of a gate report: one block per check, the partner and\n * feature tables as data lines, findings with the shared severity glyphs, and\n * the verdict last — the line a person scanning a deploy log reads first from\n * the bottom. The JSON contract never comes through here; `gate.ts` emits the\n * envelope from the same report, which is what keeps the two views one\n * derivation.\n */\nimport { SEVERITY_MARK, type Finding } from \"../../core/vocabulary/report.js\";\nimport type { GateReport, GateVerdict } from \"../../core/gate/report.js\";\nimport { plural } from \"../policy/format.js\";\n\nfunction printFindings(findings: Finding[]): void {\n for (const f of findings) {\n console.log(` ${SEVERITY_MARK[f.severity]} ${f.message}`);\n for (const d of f.details ?? []) console.log(` - ${d}`);\n }\n}\n\nexport function printGate(\n report: GateReport,\n verdict: GateVerdict,\n summary: { errors: number; warnings: number },\n): void {\n console.log(`gate — ${report.service} (a query over recorded evidence; it executes nothing)`);\n\n const check = (name: (typeof report.checks)[number][\"check\"]): Finding[] =>\n report.checks.find((c) => c.check === name)?.findings ?? [];\n\n console.log(`\\npartners — landscape ${report.landscape}`);\n for (const p of report.partners) {\n const rung = p.external ? \"external\" : (p.maturity ?? \"no services/ directory\");\n console.log(` ${p.service} · ${rung} · ${p.role} · ${p.via.join(\", \")}`);\n }\n if (report.partners.length === 0 && report.landscape === \"read\") {\n console.log(\" (the landscape draws no join into or out of this service)\");\n }\n printFindings(check(\"partners\"));\n\n console.log(\"\\nfreshness\");\n const freshness = check(\"freshness\");\n if (freshness.length === 0) console.log(` ${SEVERITY_MARK.ok} nothing recorded has gone stale`);\n printFindings(freshness);\n\n console.log(\"\\nverification\");\n for (const f of report.features) {\n const attested = f.attested > 0 ? `, ${f.attested} on an agent's word` : \"\";\n console.log(` ${f.id} · ${f.verdict} (${f.state}; ${f.confirmed}/${f.claims} confirmed${attested})`);\n }\n if (report.features.length === 0) {\n console.log(\" (no active feature carries a delta for this service)\");\n }\n printFindings(check(\"verification\"));\n\n console.log(\"\\ninterrupted\");\n const interrupted = check(\"interrupted\");\n if (interrupted.length === 0) console.log(` ${SEVERITY_MARK.ok} no interrupted commit in the docs repo`);\n printFindings(interrupted);\n\n console.log(\n `\\nverdict: ${verdict} — ${plural(summary.errors, \"error\")}, ${plural(summary.warnings, \"warning\")}` +\n (verdict === \"pass\" && summary.warnings > 0\n ? \" (advisory — `--strict` turns warnings into exit 1)\"\n : \"\"),\n );\n}\n"]}
|