@ybotok/loam 0.1.0-beta.3 → 0.2.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5992 -82
- package/COMPARISON.md +168 -63
- package/CONTRIBUTING.md +121 -0
- package/MIGRATING-from-OpenSpec.md +238 -55
- package/QUICKSTART.md +302 -0
- package/README.md +653 -144
- package/ROADMAP.md +1035 -0
- package/SCHEMA.md +3916 -263
- package/SECURITY.md +21 -3
- package/WORKFLOW.md +927 -0
- package/dist/cli.js +133 -21
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +204 -0
- package/dist/commands/adopt/adopt.js.map +1 -0
- package/dist/commands/adopt/render.js +309 -0
- package/dist/commands/adopt/render.js.map +1 -0
- package/dist/commands/archive/archive.js +74 -0
- package/dist/commands/archive/archive.js.map +1 -0
- package/dist/commands/archive/closing.js +42 -0
- package/dist/commands/archive/closing.js.map +1 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js +172 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/consumers.js +56 -0
- package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +193 -0
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
- package/dist/commands/archive/plan/document/head.js +151 -0
- package/dist/commands/archive/plan/document/head.js.map +1 -0
- package/dist/commands/archive/plan/document/links.js +284 -0
- package/dist/commands/archive/plan/document/links.js.map +1 -0
- package/dist/commands/archive/plan/document/run.js +136 -0
- package/dist/commands/archive/plan/document/run.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +319 -0
- package/dist/commands/archive/plan/gate.js.map +1 -0
- package/dist/commands/archive/plan/landscape.js +298 -0
- package/dist/commands/archive/plan/landscape.js.map +1 -0
- package/dist/commands/archive/plan/model/extending.js +85 -0
- package/dist/commands/archive/plan/model/extending.js.map +1 -0
- package/dist/commands/archive/plan/refusal.js +106 -0
- package/dist/commands/archive/plan/refusal.js.map +1 -0
- package/dist/commands/archive/plan/specs.js +318 -0
- package/dist/commands/archive/plan/specs.js.map +1 -0
- package/dist/commands/archive/plan/state.js +30 -0
- package/dist/commands/archive/plan/state.js.map +1 -0
- package/dist/commands/archive/run.js +355 -0
- package/dist/commands/archive/run.js.map +1 -0
- package/dist/commands/archive/shipped.js +49 -0
- package/dist/commands/archive/shipped.js.map +1 -0
- package/dist/commands/context/context.js +166 -0
- package/dist/commands/context/context.js.map +1 -0
- package/dist/commands/context/print.js +305 -0
- package/dist/commands/context/print.js.map +1 -0
- package/dist/commands/delta/delta.js +367 -0
- package/dist/commands/delta/delta.js.map +1 -0
- package/dist/commands/delta/print.js +221 -0
- package/dist/commands/delta/print.js.map +1 -0
- package/dist/commands/dependencies.js +6 -8
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +86 -0
- package/dist/commands/diff/diff.js.map +1 -0
- package/dist/commands/diff/print.js +38 -0
- package/dist/commands/diff/print.js.map +1 -0
- package/dist/commands/doctor.js +65 -5
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain/explain.js +227 -0
- package/dist/commands/explain/explain.js.map +1 -0
- package/dist/commands/explore.js +39 -28
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +152 -0
- package/dist/commands/gate/gate.js.map +1 -0
- package/dist/commands/gate/print.js +66 -0
- package/dist/commands/gate/print.js.map +1 -0
- package/dist/commands/gherkin/commit.js +105 -0
- package/dist/commands/gherkin/commit.js.map +1 -0
- package/dist/commands/gherkin/gherkin.js +262 -0
- package/dist/commands/gherkin/gherkin.js.map +1 -0
- package/dist/commands/gherkin/reconcile.js +146 -0
- package/dist/commands/gherkin/reconcile.js.map +1 -0
- package/dist/commands/gherkin/render.js +77 -0
- package/dist/commands/gherkin/render.js.map +1 -0
- package/dist/commands/init/example.js +204 -0
- package/dist/commands/init/example.js.map +1 -0
- package/dist/commands/init/first-hour.js +72 -0
- package/dist/commands/init/first-hour.js.map +1 -0
- package/dist/commands/{init.js → init/init.js} +142 -71
- package/dist/commands/init/init.js.map +1 -0
- package/dist/commands/init/mcp.js +131 -0
- package/dist/commands/init/mcp.js.map +1 -0
- package/dist/commands/init/options.js +78 -0
- package/dist/commands/init/options.js.map +1 -0
- package/dist/commands/instructions.js +72 -14
- package/dist/commands/instructions.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +63 -0
- package/dist/commands/list/campaign/campaign.js.map +1 -0
- package/dist/commands/list/campaign/owners.js +65 -0
- package/dist/commands/list/campaign/owners.js.map +1 -0
- package/dist/commands/list/json.js +155 -0
- package/dist/commands/list/json.js.map +1 -0
- package/dist/commands/list/list.js +358 -0
- package/dist/commands/list/list.js.map +1 -0
- package/dist/commands/list/print.js +344 -0
- package/dist/commands/list/print.js.map +1 -0
- package/dist/commands/list/review.js +120 -0
- package/dist/commands/list/review.js.map +1 -0
- package/dist/commands/list/suites/suites.js +140 -0
- package/dist/commands/list/suites/suites.js.map +1 -0
- package/dist/commands/list/views.js +108 -0
- package/dist/commands/list/views.js.map +1 -0
- package/dist/commands/mcp/dispatch.js +238 -0
- package/dist/commands/mcp/dispatch.js.map +1 -0
- package/dist/commands/mcp/mcp.js +75 -0
- package/dist/commands/mcp/mcp.js.map +1 -0
- package/dist/commands/mcp/serve.js +55 -0
- package/dist/commands/mcp/serve.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/active.js +175 -0
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
- package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/living.js +62 -0
- package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/stage.js +143 -0
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/target.js +219 -0
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js +86 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/error.js +18 -0
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js +162 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/paths.js +40 -0
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/read.js +49 -0
- package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
- package/dist/commands/migrate-openspec/print.js +89 -0
- package/dist/commands/migrate-openspec/print.js.map +1 -0
- package/dist/commands/new/capability-template.js +118 -0
- package/dist/commands/new/capability-template.js.map +1 -0
- package/dist/commands/new/commit.js +62 -0
- package/dist/commands/new/commit.js.map +1 -0
- package/dist/commands/new/modifies.js +206 -0
- package/dist/commands/new/modifies.js.map +1 -0
- package/dist/commands/new/new.js +328 -0
- package/dist/commands/new/new.js.map +1 -0
- package/dist/commands/new/templates.js +279 -0
- package/dist/commands/new/templates.js.map +1 -0
- package/dist/commands/open.js +199 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/policy/format.js +152 -0
- package/dist/commands/policy/format.js.map +1 -0
- package/dist/commands/{docs-repo-gate.js → policy/gate.js} +38 -11
- package/dist/commands/policy/gate.js.map +1 -0
- package/dist/commands/rebase/edit.js +106 -0
- package/dist/commands/rebase/edit.js.map +1 -0
- package/dist/commands/rebase/living.js +185 -0
- package/dist/commands/rebase/living.js.map +1 -0
- package/dist/commands/rebase/plan.js +245 -0
- package/dist/commands/rebase/plan.js.map +1 -0
- package/dist/commands/rebase/rebase.js +300 -0
- package/dist/commands/rebase/rebase.js.map +1 -0
- package/dist/commands/seed/commit.js +221 -0
- package/dist/commands/seed/commit.js.map +1 -0
- package/dist/commands/seed/plan.js +278 -0
- package/dist/commands/seed/plan.js.map +1 -0
- package/dist/commands/seed/seed.js +174 -0
- package/dist/commands/seed/seed.js.map +1 -0
- package/dist/commands/show/feature.js +182 -0
- package/dist/commands/show/feature.js.map +1 -0
- package/dist/commands/show/introduced.js +157 -0
- package/dist/commands/show/introduced.js.map +1 -0
- package/dist/commands/show/marks.js +53 -0
- package/dist/commands/show/marks.js.map +1 -0
- package/dist/commands/show/service.js +239 -0
- package/dist/commands/show/service.js.map +1 -0
- package/dist/commands/show/show.js +87 -0
- package/dist/commands/show/show.js.map +1 -0
- package/dist/commands/status/fleet-next.js +90 -0
- package/dist/commands/status/fleet-next.js.map +1 -0
- package/dist/commands/status/json.js +47 -0
- package/dist/commands/status/json.js.map +1 -0
- package/dist/commands/status/print.js +209 -0
- package/dist/commands/status/print.js.map +1 -0
- package/dist/commands/status/status.js +115 -0
- package/dist/commands/status/status.js.map +1 -0
- package/dist/commands/steps/steps.js +269 -0
- package/dist/commands/steps/steps.js.map +1 -0
- package/dist/commands/subsystem/edit.js +192 -0
- package/dist/commands/subsystem/edit.js.map +1 -0
- package/dist/commands/subsystem/move.js +325 -0
- package/dist/commands/subsystem/move.js.map +1 -0
- package/dist/commands/subsystem/query.js +113 -0
- package/dist/commands/subsystem/query.js.map +1 -0
- package/dist/commands/subsystem/subsystem.js +98 -0
- package/dist/commands/subsystem/subsystem.js.map +1 -0
- package/dist/commands/subsystem/sync.js +318 -0
- package/dist/commands/subsystem/sync.js.map +1 -0
- package/dist/commands/subsystem/txn/exclude.js +132 -0
- package/dist/commands/subsystem/txn/exclude.js.map +1 -0
- package/dist/commands/subsystem/txn/links.js +128 -0
- package/dist/commands/subsystem/txn/links.js.map +1 -0
- package/dist/commands/subsystem/txn/report.js +174 -0
- package/dist/commands/subsystem/txn/report.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +196 -0
- package/dist/commands/subsystem/txn/txn.js.map +1 -0
- package/dist/commands/subsystem/txn/views.js +249 -0
- package/dist/commands/subsystem/txn/views.js.map +1 -0
- package/dist/commands/tasks/actions.js +107 -0
- package/dist/commands/tasks/actions.js.map +1 -0
- package/dist/commands/tasks/render.js +253 -0
- package/dist/commands/tasks/render.js.map +1 -0
- package/dist/commands/tasks/tasks.js +119 -0
- package/dist/commands/tasks/tasks.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +176 -0
- package/dist/commands/unarchive/manifest.js.map +1 -0
- package/dist/commands/unarchive/restore.js +254 -0
- package/dist/commands/unarchive/restore.js.map +1 -0
- package/dist/commands/unarchive/unarchive.js +59 -0
- package/dist/commands/unarchive/unarchive.js.map +1 -0
- package/dist/commands/validate/arch-coverage.js +245 -0
- package/dist/commands/validate/arch-coverage.js.map +1 -0
- package/dist/commands/validate/checks/fleet-shape.js +204 -0
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
- package/dist/commands/validate/checks/health-deps.js +58 -0
- package/dist/commands/validate/checks/health-deps.js.map +1 -0
- package/dist/commands/validate/checks/introduced.js +102 -0
- package/dist/commands/validate/checks/introduced.js.map +1 -0
- package/dist/commands/validate/checks/requirements.js +264 -0
- package/dist/commands/validate/checks/requirements.js.map +1 -0
- package/dist/commands/validate/checks/sources.js +85 -0
- package/dist/commands/validate/checks/sources.js.map +1 -0
- package/dist/commands/validate/coverage/covers-after.js +105 -0
- package/dist/commands/validate/coverage/covers-after.js.map +1 -0
- package/dist/commands/validate/feature.js +222 -0
- package/dist/commands/validate/feature.js.map +1 -0
- package/dist/commands/validate/fleet/census.js +93 -0
- package/dist/commands/validate/fleet/census.js.map +1 -0
- package/dist/commands/validate/fleet/kind-tags.js +39 -0
- package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
- package/dist/commands/validate/fleet/landscape.js +371 -0
- package/dist/commands/validate/fleet/landscape.js.map +1 -0
- package/dist/commands/validate/fleet/load.js +118 -0
- package/dist/commands/validate/fleet/load.js.map +1 -0
- package/dist/commands/validate/fleet/map/attest.js +98 -0
- package/dist/commands/validate/fleet/map/attest.js.map +1 -0
- package/dist/commands/validate/fleet/map/bindings.js +107 -0
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
- package/dist/commands/validate/fleet/map/consumers.js +244 -0
- package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
- package/dist/commands/validate/fleet/map/isolation.js +116 -0
- package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
- package/dist/commands/validate/fleet/map/usecases.js +54 -0
- package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
- package/dist/commands/validate/fleet/obligations.js +266 -0
- package/dist/commands/validate/fleet/obligations.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/print.js +71 -0
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js +162 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/place.js +13 -0
- package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
- package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
- package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
- package/dist/commands/validate/fleet/views/ids.js +75 -0
- package/dist/commands/validate/fleet/views/ids.js.map +1 -0
- package/dist/commands/validate/fleet/views/projects.js +278 -0
- package/dist/commands/validate/fleet/views/projects.js.map +1 -0
- package/dist/commands/validate/fleet/views/root-documents.js +83 -0
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +52 -0
- package/dist/commands/validate/fleet/views/stale.js.map +1 -0
- package/dist/commands/validate/links/corpus.js +89 -0
- package/dist/commands/validate/links/corpus.js.map +1 -0
- package/dist/commands/validate/links/glossary.js +75 -0
- package/dist/commands/validate/links/glossary.js.map +1 -0
- package/dist/commands/validate/render/repeats.js +63 -0
- package/dist/commands/validate/render/repeats.js.map +1 -0
- package/dist/commands/validate/render/rollups.js +115 -0
- package/dist/commands/validate/render/rollups.js.map +1 -0
- package/dist/commands/validate/render/text.js +199 -0
- package/dist/commands/validate/render/text.js.map +1 -0
- package/dist/commands/validate/report.js +145 -0
- package/dist/commands/validate/report.js.map +1 -0
- package/dist/commands/validate/scope.js +113 -0
- package/dist/commands/validate/scope.js.map +1 -0
- package/dist/commands/validate/service/api.js +252 -0
- package/dist/commands/validate/service/api.js.map +1 -0
- package/dist/commands/validate/service/contracts/generated.js +134 -0
- package/dist/commands/validate/service/contracts/generated.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +241 -0
- package/dist/commands/validate/service/events/events.js.map +1 -0
- package/dist/commands/validate/service/events/message-producers.js +77 -0
- package/dist/commands/validate/service/events/message-producers.js.map +1 -0
- package/dist/commands/validate/service/evidence-pins.js +177 -0
- package/dist/commands/validate/service/evidence-pins.js.map +1 -0
- package/dist/commands/validate/service/model/diverged.js +226 -0
- package/dist/commands/validate/service/model/diverged.js.map +1 -0
- package/dist/commands/validate/service/model/grade.js +163 -0
- package/dist/commands/validate/service/model/grade.js.map +1 -0
- package/dist/commands/validate/service/service.js +310 -0
- package/dist/commands/validate/service/service.js.map +1 -0
- package/dist/commands/validate/service/specs.js +279 -0
- package/dist/commands/validate/service/specs.js.map +1 -0
- package/dist/commands/validate/service/spine.js +148 -0
- package/dist/commands/validate/service/spine.js.map +1 -0
- package/dist/commands/validate/service/usecases/tags.js +113 -0
- package/dist/commands/validate/service/usecases/tags.js.map +1 -0
- package/dist/commands/validate/service/usecases/usecases.js +209 -0
- package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
- package/dist/commands/validate/validate.js +363 -0
- package/dist/commands/validate/validate.js.map +1 -0
- package/dist/commands/verify/cross/diff.js +178 -0
- package/dist/commands/verify/cross/diff.js.map +1 -0
- package/dist/commands/verify/evidence/contract.js +32 -0
- package/dist/commands/verify/evidence/contract.js.map +1 -0
- package/dist/commands/verify/evidence/pins.js +52 -0
- package/dist/commands/verify/evidence/pins.js.map +1 -0
- package/dist/commands/verify/evidence/read.js +81 -0
- package/dist/commands/verify/evidence/read.js.map +1 -0
- package/dist/commands/verify/frozen.js +263 -0
- package/dist/commands/verify/frozen.js.map +1 -0
- package/dist/commands/verify/record.js +278 -0
- package/dist/commands/verify/record.js.map +1 -0
- package/dist/commands/verify/report.js +206 -0
- package/dist/commands/verify/report.js.map +1 -0
- package/dist/commands/verify/results.js +328 -0
- package/dist/commands/verify/results.js.map +1 -0
- package/dist/commands/verify/verify.js +220 -0
- package/dist/commands/verify/verify.js.map +1 -0
- package/dist/commands/vouch/contract.js +11 -0
- package/dist/commands/vouch/contract.js.map +1 -0
- package/dist/commands/vouch/pack/contract.js +2 -0
- package/dist/commands/vouch/pack/contract.js.map +1 -0
- package/dist/commands/vouch/pack/pack.js +266 -0
- package/dist/commands/vouch/pack/pack.js.map +1 -0
- package/dist/commands/vouch/pack/print.js +266 -0
- package/dist/commands/vouch/pack/print.js.map +1 -0
- package/dist/commands/vouch/pack/sections.js +141 -0
- package/dist/commands/vouch/pack/sections.js.map +1 -0
- package/dist/commands/vouch/prompt/confirm.js +46 -0
- package/dist/commands/vouch/prompt/confirm.js.map +1 -0
- package/dist/commands/vouch/report.js +101 -0
- package/dist/commands/vouch/report.js.map +1 -0
- package/dist/commands/vouch/run.js +230 -0
- package/dist/commands/vouch/run.js.map +1 -0
- package/dist/commands/vouch/sample/plan.js +111 -0
- package/dist/commands/vouch/sample/plan.js.map +1 -0
- package/dist/commands/vouch/sample/print.js +109 -0
- package/dist/commands/vouch/sample/print.js.map +1 -0
- package/dist/commands/vouch/vet/verify.js +155 -0
- package/dist/commands/vouch/vet/verify.js.map +1 -0
- package/dist/commands/vouch/vouch.js +169 -0
- package/dist/commands/vouch/vouch.js.map +1 -0
- package/dist/core/agent/agents-md/command-map.js +299 -0
- package/dist/core/agent/agents-md/command-map.js.map +1 -0
- package/dist/core/agent/agents-md/map/explain.js +46 -0
- package/dist/core/agent/agents-md/map/explain.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/context.js +50 -0
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js +59 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js +65 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
- package/dist/core/agent/agents-md/map/mcp.js +44 -0
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
- package/dist/core/agent/agents-md/map/status.js +78 -0
- package/dist/core/agent/agents-md/map/status.js.map +1 -0
- package/dist/core/agent/agents-md/map/subsystem.js +104 -0
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
- package/dist/core/agent/agents-md/map/usecases.js +138 -0
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
- package/dist/core/agent/agents-md/refusals.js +273 -0
- package/dist/core/agent/agents-md/refusals.js.map +1 -0
- package/dist/core/agent/agents-md.js +203 -0
- package/dist/core/agent/agents-md.js.map +1 -0
- package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +60 -0
- package/dist/core/agent/agents-stamp.js.map +1 -0
- package/dist/core/agent/contract.js +2 -0
- package/dist/core/agent/contract.js.map +1 -0
- package/dist/core/agent/protocol.js +193 -0
- package/dist/core/agent/protocol.js.map +1 -0
- package/dist/core/agent/scaffold.js +254 -0
- package/dist/core/agent/scaffold.js.map +1 -0
- package/dist/core/agent/support/report.js +144 -0
- package/dist/core/agent/support/report.js.map +1 -0
- package/dist/core/agent/tools/dialects.js +151 -0
- package/dist/core/agent/tools/dialects.js.map +1 -0
- package/dist/core/agent/tools/registry.js +193 -0
- package/dist/core/agent/tools/registry.js.map +1 -0
- package/dist/core/agent/workflows/adopt.js +280 -0
- package/dist/core/agent/workflows/adopt.js.map +1 -0
- package/dist/core/agent/workflows/check.js +373 -0
- package/dist/core/agent/workflows/check.js.map +1 -0
- package/dist/core/agent/workflows/closing.js +323 -0
- package/dist/core/agent/workflows/closing.js.map +1 -0
- package/dist/core/agent/workflows/feature.js +220 -0
- package/dist/core/agent/workflows/feature.js.map +1 -0
- package/dist/core/agent/workflows/implement.js +188 -0
- package/dist/core/agent/workflows/implement.js.map +1 -0
- package/dist/core/agent/workflows/reference/authoring.js +311 -0
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
- package/dist/core/agent/workflows/reference/codes.js +67 -0
- package/dist/core/agent/workflows/reference/codes.js.map +1 -0
- package/dist/core/agent/workflows/reference/done-check.js +210 -0
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
- package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
- package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
- package/dist/core/agent/workflows/reference/reference.js +19 -0
- package/dist/core/agent/workflows/reference/reference.js.map +1 -0
- package/dist/core/agent/workflows/reference/spine.js +338 -0
- package/dist/core/agent/workflows/reference/spine.js.map +1 -0
- package/dist/core/asyncapi/baseline/plan.js +43 -0
- package/dist/core/asyncapi/baseline/plan.js.map +1 -0
- package/dist/core/asyncapi/baseline/surfaces.js +54 -0
- package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
- package/dist/core/asyncapi/depth.js +164 -0
- package/dist/core/asyncapi/depth.js.map +1 -0
- package/dist/core/asyncapi/digest.js +146 -0
- package/dist/core/asyncapi/digest.js.map +1 -0
- package/dist/core/asyncapi/merge/components.js +92 -0
- package/dist/core/asyncapi/merge/components.js.map +1 -0
- package/dist/core/asyncapi/merge/error.js +38 -0
- package/dist/core/asyncapi/merge/error.js.map +1 -0
- package/dist/core/asyncapi/merge/markers.js +290 -0
- package/dist/core/asyncapi/merge/markers.js.map +1 -0
- package/dist/core/asyncapi/merge/merge.js +195 -0
- package/dist/core/asyncapi/merge/merge.js.map +1 -0
- package/dist/core/asyncapi/merge/pin.js +83 -0
- package/dist/core/asyncapi/merge/pin.js.map +1 -0
- package/dist/core/asyncapi/model.js +11 -0
- package/dist/core/asyncapi/model.js.map +1 -0
- package/dist/core/asyncapi/producers.js +50 -0
- package/dist/core/asyncapi/producers.js.map +1 -0
- package/dist/core/asyncapi/read.js +263 -0
- package/dist/core/asyncapi/read.js.map +1 -0
- package/dist/core/brief/brief.js +285 -0
- package/dist/core/brief/brief.js.map +1 -0
- package/dist/core/brief/checks.js +385 -0
- package/dist/core/brief/checks.js.map +1 -0
- package/dist/core/brief/landscape.js +273 -0
- package/dist/core/brief/landscape.js.map +1 -0
- package/dist/core/brief/map/extend.js +84 -0
- package/dist/core/brief/map/extend.js.map +1 -0
- package/dist/core/brief/map/owed.js +266 -0
- package/dist/core/brief/map/owed.js.map +1 -0
- package/dist/core/brief/targets.js +304 -0
- package/dist/core/brief/targets.js.map +1 -0
- package/dist/core/brief/unchecked.js +58 -0
- package/dist/core/brief/unchecked.js.map +1 -0
- package/dist/core/c4/arch.js +190 -0
- package/dist/core/c4/arch.js.map +1 -0
- package/dist/core/c4/direction/direction.js +114 -0
- package/dist/core/c4/direction/direction.js.map +1 -0
- package/dist/core/c4/likec4.js +243 -0
- package/dist/core/c4/likec4.js.map +1 -0
- package/dist/core/c4/locate/unlocated.js +150 -0
- package/dist/core/c4/locate/unlocated.js.map +1 -0
- package/dist/core/c4/parsed/deployment.js +160 -0
- package/dist/core/c4/parsed/deployment.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js +132 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
- package/dist/core/c4/parsed/specification.js +78 -0
- package/dist/core/c4/parsed/specification.js.map +1 -0
- package/dist/core/c4/parsed/values.js +110 -0
- package/dist/core/c4/parsed/values.js.map +1 -0
- package/dist/core/c4/parsed/view-ids.js +113 -0
- package/dist/core/c4/parsed/view-ids.js.map +1 -0
- package/dist/core/c4/prefetch/fleet.js +134 -0
- package/dist/core/c4/prefetch/fleet.js.map +1 -0
- package/dist/core/c4/project/architecture.js +161 -0
- package/dist/core/c4/project/architecture.js.map +1 -0
- package/dist/core/c4/project/batch.js +140 -0
- package/dist/core/c4/project/batch.js.map +1 -0
- package/dist/core/c4/project/documents.js +89 -0
- package/dist/core/c4/project/documents.js.map +1 -0
- package/dist/core/c4/project/load.js +219 -0
- package/dist/core/c4/project/load.js.map +1 -0
- package/dist/core/c4/project/staged.js +277 -0
- package/dist/core/c4/project/staged.js.map +1 -0
- package/dist/core/c4/resolve/attested.js +0 -0
- package/dist/core/c4/resolve/attested.js.map +1 -0
- package/dist/core/c4/resolve/external.js +51 -0
- package/dist/core/c4/resolve/external.js.map +1 -0
- package/dist/core/c4/resolve/service.js +174 -0
- package/dist/core/c4/resolve/service.js.map +1 -0
- package/dist/core/c4/resolve/steps.js +290 -0
- package/dist/core/c4/resolve/steps.js.map +1 -0
- package/dist/core/c4/resolve/systems.js +154 -0
- package/dist/core/c4/resolve/systems.js.map +1 -0
- package/dist/core/c4/root-project/exclude.js +352 -0
- package/dist/core/c4/root-project/exclude.js.map +1 -0
- package/dist/core/c4/seed/fleet-file.js +201 -0
- package/dist/core/c4/seed/fleet-file.js.map +1 -0
- package/dist/core/c4/seed/items.js +91 -0
- package/dist/core/c4/seed/items.js.map +1 -0
- package/dist/core/c4/seed/stamp.js +48 -0
- package/dist/core/c4/seed/stamp.js.map +1 -0
- package/dist/core/c4/seed/template.js +173 -0
- package/dist/core/c4/seed/template.js.map +1 -0
- package/dist/core/c4/service-model/created/models.js +96 -0
- package/dist/core/c4/service-model/created/models.js.map +1 -0
- package/dist/core/c4/service-model/documents.js +47 -0
- package/dist/core/c4/service-model/documents.js.map +1 -0
- package/dist/core/c4/service-model/fleet/extending.js +82 -0
- package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
- package/dist/core/c4/service-model/load.js +165 -0
- package/dist/core/c4/service-model/load.js.map +1 -0
- package/dist/core/c4/service-model/renderer.js +128 -0
- package/dist/core/c4/service-model/renderer.js.map +1 -0
- package/dist/core/c4/service-model/shape.js +106 -0
- package/dist/core/c4/service-model/shape.js.map +1 -0
- package/dist/core/c4/service-model/slice.js +188 -0
- package/dist/core/c4/service-model/slice.js.map +1 -0
- package/dist/core/c4/source-mask.js +369 -0
- package/dist/core/c4/source-mask.js.map +1 -0
- package/dist/core/c4/source-scan.js +308 -0
- package/dist/core/c4/source-scan.js.map +1 -0
- package/dist/core/c4/splice/authored-source.js +167 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -0
- package/dist/core/c4/splice/contract.js +30 -0
- package/dist/core/c4/splice/contract.js.map +1 -0
- package/dist/core/c4/splice/delta-blocks.js +126 -0
- package/dist/core/c4/splice/delta-blocks.js.map +1 -0
- package/dist/core/c4/splice/identity/declared.js +87 -0
- package/dist/core/c4/splice/identity/declared.js.map +1 -0
- package/dist/core/c4/splice/identity/edges.js +73 -0
- package/dist/core/c4/splice/identity/edges.js.map +1 -0
- package/dist/core/c4/splice/identity/existing.js +80 -0
- package/dist/core/c4/splice/identity/existing.js.map +1 -0
- package/dist/core/c4/splice/identity/interior.js +80 -0
- package/dist/core/c4/splice/identity/interior.js.map +1 -0
- package/dist/core/c4/splice/identity/owner.js +50 -0
- package/dist/core/c4/splice/identity/owner.js.map +1 -0
- package/dist/core/c4/splice/landscape-merge.js +337 -0
- package/dist/core/c4/splice/landscape-merge.js.map +1 -0
- package/dist/core/c4/splice/model/merge.js +151 -0
- package/dist/core/c4/splice/model/merge.js.map +1 -0
- package/dist/core/c4/splice/placement.js +298 -0
- package/dist/core/c4/splice/placement.js.map +1 -0
- package/dist/core/c4/splice/text/spot.js +131 -0
- package/dist/core/c4/splice/text/spot.js.map +1 -0
- package/dist/core/c4/workspace.js +166 -0
- package/dist/core/c4/workspace.js.map +1 -0
- package/dist/core/capabilities/authoring/notes.js +141 -0
- package/dist/core/capabilities/authoring/notes.js.map +1 -0
- package/dist/core/capabilities/capabilities.js +124 -0
- package/dist/core/capabilities/capabilities.js.map +1 -0
- package/dist/core/capabilities/delta/doc.js +47 -0
- package/dist/core/capabilities/delta/doc.js.map +1 -0
- package/dist/core/capabilities/delta/overlay.js +129 -0
- package/dist/core/capabilities/delta/overlay.js.map +1 -0
- package/dist/core/capabilities/delta/summary.js +57 -0
- package/dist/core/capabilities/delta/summary.js.map +1 -0
- package/dist/core/capabilities/delta/tree.js +43 -0
- package/dist/core/capabilities/delta/tree.js.map +1 -0
- package/dist/core/capabilities/delta/uncovered.js +222 -0
- package/dist/core/capabilities/delta/uncovered.js.map +1 -0
- package/dist/core/capabilities/findings.js +338 -0
- package/dist/core/capabilities/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/findings.js +174 -0
- package/dist/core/capabilities/realizes/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/join.js +123 -0
- package/dist/core/capabilities/realizes/join.js.map +1 -0
- package/dist/core/capabilities/rollup.js +205 -0
- package/dist/core/capabilities/rollup.js.map +1 -0
- package/dist/core/capabilities/tree.js +104 -0
- package/dist/core/capabilities/tree.js.map +1 -0
- package/dist/core/capabilities/usecase-join.js +267 -0
- package/dist/core/capabilities/usecase-join.js.map +1 -0
- package/dist/core/coherence/authoring/scaffold.js +185 -0
- package/dist/core/coherence/authoring/scaffold.js.map +1 -0
- package/dist/core/coherence/authoring/sentinels.js +75 -0
- package/dist/core/coherence/authoring/sentinels.js.map +1 -0
- package/dist/core/coherence/calls/called.js +52 -0
- package/dist/core/coherence/calls/called.js.map +1 -0
- package/dist/core/coherence/coherence.js +332 -0
- package/dist/core/coherence/coherence.js.map +1 -0
- package/dist/core/coherence/consumed/operations.js +70 -0
- package/dist/core/coherence/consumed/operations.js.map +1 -0
- package/dist/core/coherence/consumed/refusal.js +38 -0
- package/dist/core/coherence/consumed/refusal.js.map +1 -0
- package/dist/core/coherence/covers/health.js +85 -0
- package/dist/core/coherence/covers/health.js.map +1 -0
- package/dist/core/coherence/declared.js +295 -0
- package/dist/core/coherence/declared.js.map +1 -0
- package/dist/core/coherence/events/declared.js +191 -0
- package/dist/core/coherence/events/declared.js.map +1 -0
- package/dist/core/coherence/events/events.js +335 -0
- package/dist/core/coherence/events/events.js.map +1 -0
- package/dist/core/coherence/events/grades.js +189 -0
- package/dist/core/coherence/events/grades.js.map +1 -0
- package/dist/core/coherence/events/lookups.js +63 -0
- package/dist/core/coherence/events/lookups.js.map +1 -0
- package/dist/core/coherence/events/produced.js +100 -0
- package/dist/core/coherence/events/produced.js.map +1 -0
- package/dist/core/coherence/living.js +198 -0
- package/dist/core/coherence/living.js.map +1 -0
- package/dist/core/coherence/lookups.js +95 -0
- package/dist/core/coherence/lookups.js.map +1 -0
- package/dist/core/coherence/pending.js +39 -0
- package/dist/core/coherence/pending.js.map +1 -0
- package/dist/core/coherence/retired/archived.js +285 -0
- package/dist/core/coherence/retired/archived.js.map +1 -0
- package/dist/core/coherence/retired/event-pair.js +62 -0
- package/dist/core/coherence/retired/event-pair.js.map +1 -0
- package/dist/core/coherence/retired/merged.js +79 -0
- package/dist/core/coherence/retired/merged.js.map +1 -0
- package/dist/core/coherence/retired/retirement.js +49 -0
- package/dist/core/coherence/retired/retirement.js.map +1 -0
- package/dist/core/coherence/retired/revival/arrivals.js +131 -0
- package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
- package/dist/core/coherence/retired/revival/revived.js +297 -0
- package/dist/core/coherence/retired/revival/revived.js.map +1 -0
- package/dist/core/conflict-markers.js +60 -0
- package/dist/core/conflict-markers.js.map +1 -0
- package/dist/core/consumers/after.js +113 -0
- package/dist/core/consumers/after.js.map +1 -0
- package/dist/core/consumers/consumers.js +296 -0
- package/dist/core/consumers/consumers.js.map +1 -0
- package/dist/core/consumers/current.js +211 -0
- package/dist/core/consumers/current.js.map +1 -0
- package/dist/core/consumers/scan.js +46 -0
- package/dist/core/consumers/scan.js.map +1 -0
- package/dist/core/consumers/state.js +27 -0
- package/dist/core/consumers/state.js.map +1 -0
- package/dist/core/delta/claims.js +73 -0
- package/dist/core/delta/claims.js.map +1 -0
- package/dist/core/delta/delta.js +341 -0
- package/dist/core/delta/delta.js.map +1 -0
- package/dist/core/delta/document.js +177 -0
- package/dist/core/delta/document.js.map +1 -0
- package/dist/core/delta/scope.js +48 -0
- package/dist/core/delta/scope.js.map +1 -0
- package/dist/core/delta/select.js +237 -0
- package/dist/core/delta/select.js.map +1 -0
- package/dist/core/dependencies/dependencies.js +141 -0
- package/dist/core/dependencies/dependencies.js.map +1 -0
- package/dist/core/dependencies/facts.js +165 -0
- package/dist/core/dependencies/facts.js.map +1 -0
- package/dist/core/dependencies/fanin.js +88 -0
- package/dist/core/dependencies/fanin.js.map +1 -0
- package/dist/core/dependencies/graph.js +106 -0
- package/dist/core/dependencies/graph.js.map +1 -0
- package/dist/core/deployment/delta.js +191 -0
- package/dist/core/deployment/delta.js.map +1 -0
- package/dist/core/deployment/objects.js +145 -0
- package/dist/core/deployment/objects.js.map +1 -0
- package/dist/core/diff/base-git.js +230 -0
- package/dist/core/diff/base-git.js.map +1 -0
- package/dist/core/diff/base-state.js +237 -0
- package/dist/core/diff/base-state.js.map +1 -0
- package/dist/core/diff/joins.js +136 -0
- package/dist/core/diff/joins.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +204 -0
- package/dist/core/diff/scope/changed-paths.js.map +1 -0
- package/dist/core/diff/scope/edges.js +82 -0
- package/dist/core/diff/scope/edges.js.map +1 -0
- package/dist/core/diff/semantic.js +312 -0
- package/dist/core/diff/semantic.js.map +1 -0
- package/dist/core/docs.js +37 -85
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +238 -0
- package/dist/core/doctor/agents.js.map +1 -0
- package/dist/core/doctor/config.js +139 -0
- package/dist/core/doctor/config.js.map +1 -0
- package/dist/core/doctor/doctor.js +355 -0
- package/dist/core/doctor/doctor.js.map +1 -0
- package/dist/core/doctor/report.js +13 -0
- package/dist/core/doctor/report.js.map +1 -0
- package/dist/core/doctor/reports/scan.js +242 -0
- package/dist/core/doctor/reports/scan.js.map +1 -0
- package/dist/core/doctor/residue.js +110 -0
- package/dist/core/doctor/residue.js.map +1 -0
- package/dist/core/document/apply.js +224 -0
- package/dist/core/document/apply.js.map +1 -0
- package/dist/core/{frontmatter.js → document/frontmatter.js} +103 -2
- package/dist/core/document/frontmatter.js.map +1 -0
- package/dist/core/document/grammar/directives.js +143 -0
- package/dist/core/document/grammar/directives.js.map +1 -0
- package/dist/core/document/parse.js +286 -0
- package/dist/core/document/parse.js.map +1 -0
- package/dist/core/document/scenarios.js +238 -0
- package/dist/core/document/scenarios.js.map +1 -0
- package/dist/core/document/spec.js +234 -0
- package/dist/core/document/spec.js.map +1 -0
- package/dist/core/{config.js → envelope/config.js} +89 -18
- package/dist/core/envelope/config.js.map +1 -0
- package/dist/core/envelope/json.js +209 -0
- package/dist/core/envelope/json.js.map +1 -0
- package/dist/core/{version.js → envelope/version.js} +3 -3
- package/dist/core/envelope/version.js.map +1 -0
- package/dist/core/explain/families.js +180 -0
- package/dist/core/explain/families.js.map +1 -0
- package/dist/core/explain/fix-tables.js +178 -0
- package/dist/core/explain/fix-tables.js.map +1 -0
- package/dist/core/explain/lookup.js +194 -0
- package/dist/core/explain/lookup.js.map +1 -0
- package/dist/core/explain/refusals.js +49 -0
- package/dist/core/explain/refusals.js.map +1 -0
- package/dist/core/explain/terms/documents.js +227 -0
- package/dist/core/explain/terms/documents.js.map +1 -0
- package/dist/core/explain/terms/done-check.js +144 -0
- package/dist/core/explain/terms/done-check.js.map +1 -0
- package/dist/core/explain/terms/entry.js +14 -0
- package/dist/core/explain/terms/entry.js.map +1 -0
- package/dist/core/explain/terms/stages.js +71 -0
- package/dist/core/explain/terms/stages.js.map +1 -0
- package/dist/core/explain/terms/tasks.js +97 -0
- package/dist/core/explain/terms/tasks.js.map +1 -0
- package/dist/core/explain/terms.js +103 -0
- package/dist/core/explain/terms.js.map +1 -0
- package/dist/core/explore/describe.js +105 -0
- package/dist/core/explore/describe.js.map +1 -0
- package/dist/core/explore/explore.js +204 -0
- package/dist/core/explore/explore.js.map +1 -0
- package/dist/core/fleet-context.js +174 -146
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +208 -0
- package/dist/core/gate/checks.js.map +1 -0
- package/dist/core/gate/partners.js +246 -0
- package/dist/core/gate/partners.js.map +1 -0
- package/dist/core/gate/report.js +47 -0
- package/dist/core/gate/report.js.map +1 -0
- package/dist/core/gate/verification.js +176 -0
- package/dist/core/gate/verification.js.map +1 -0
- package/dist/core/gherkin/digest.js +60 -0
- package/dist/core/gherkin/digest.js.map +1 -0
- package/dist/core/gherkin/emit.js +177 -0
- package/dist/core/gherkin/emit.js.map +1 -0
- package/dist/core/gherkin/read.js +76 -0
- package/dist/core/gherkin/read.js.map +1 -0
- package/dist/core/gherkin/stale.js +210 -0
- package/dist/core/gherkin/stale.js.map +1 -0
- package/dist/core/gherkin/stamp.js +77 -0
- package/dist/core/gherkin/stamp.js.map +1 -0
- package/dist/core/gherkin/steps/catalogue.js +154 -0
- package/dist/core/gherkin/steps/catalogue.js.map +1 -0
- package/dist/core/gherkin/steps/inventory.js +98 -0
- package/dist/core/gherkin/steps/inventory.js.map +1 -0
- package/dist/core/gherkin/steps/phrase.js +72 -0
- package/dist/core/gherkin/steps/phrase.js.map +1 -0
- package/dist/core/glossary/delta.js +109 -0
- package/dist/core/glossary/delta.js.map +1 -0
- package/dist/core/glossary/tree.js +73 -0
- package/dist/core/glossary/tree.js.map +1 -0
- package/dist/core/{concurrency.js → kernel/concurrency.js} +19 -0
- package/dist/core/kernel/concurrency.js.map +1 -0
- package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
- package/dist/core/kernel/document-bytes.js.map +1 -0
- package/dist/core/kernel/fences.js +42 -0
- package/dist/core/kernel/fences.js.map +1 -0
- package/dist/core/kernel/ids/capability.js +72 -0
- package/dist/core/kernel/ids/capability.js.map +1 -0
- package/dist/core/kernel/ids/dirs.js +33 -0
- package/dist/core/kernel/ids/dirs.js.map +1 -0
- package/dist/core/kernel/ids/feature.js +75 -0
- package/dist/core/kernel/ids/feature.js.map +1 -0
- package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
- package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
- package/dist/core/kernel/ids/service.js +156 -0
- package/dist/core/kernel/ids/service.js.map +1 -0
- package/dist/core/kernel/ids/subsystem.js +63 -0
- package/dist/core/kernel/ids/subsystem.js.map +1 -0
- package/dist/core/{path-safety.js → kernel/path-safety.js} +56 -4
- package/dist/core/kernel/path-safety.js.map +1 -0
- package/dist/core/{records.js → kernel/records.js} +11 -0
- package/dist/core/kernel/records.js.map +1 -0
- package/dist/core/links/backlinks.js +66 -0
- package/dist/core/links/backlinks.js.map +1 -0
- package/dist/core/links/case.js +74 -0
- package/dist/core/links/case.js.map +1 -0
- package/dist/core/links/corpus.js +118 -0
- package/dist/core/links/corpus.js.map +1 -0
- package/dist/core/links/findings.js +110 -0
- package/dist/core/links/findings.js.map +1 -0
- package/dist/core/links/parse.js +265 -0
- package/dist/core/links/parse.js.map +1 -0
- package/dist/core/mcp/argv.js +212 -0
- package/dist/core/mcp/argv.js.map +1 -0
- package/dist/core/mcp/author-tools.js +73 -0
- package/dist/core/mcp/author-tools.js.map +1 -0
- package/dist/core/mcp/framing.js +46 -0
- package/dist/core/mcp/framing.js.map +1 -0
- package/dist/core/mcp/protocol.js +280 -0
- package/dist/core/mcp/protocol.js.map +1 -0
- package/dist/core/mcp/tools.js +324 -0
- package/dist/core/mcp/tools.js.map +1 -0
- package/dist/core/obligations/obligations.js +106 -0
- package/dist/core/obligations/obligations.js.map +1 -0
- package/dist/core/openapi/baseline/gate.js +207 -0
- package/dist/core/openapi/baseline/gate.js.map +1 -0
- package/dist/core/openapi/baseline/plan.js +142 -0
- package/dist/core/openapi/baseline/plan.js.map +1 -0
- package/dist/core/openapi/baseline/record.js +174 -0
- package/dist/core/openapi/baseline/record.js.map +1 -0
- package/dist/core/openapi/contract-text.js +33 -0
- package/dist/core/openapi/contract-text.js.map +1 -0
- package/dist/core/openapi/contract-tree.js +301 -0
- package/dist/core/openapi/contract-tree.js.map +1 -0
- package/dist/core/openapi/depth.js +136 -0
- package/dist/core/openapi/depth.js.map +1 -0
- package/dist/core/openapi/digest.js +239 -0
- package/dist/core/openapi/digest.js.map +1 -0
- package/dist/core/{openapi.js → openapi/doc.js} +54 -108
- package/dist/core/openapi/doc.js.map +1 -0
- package/dist/core/openapi/merge/components.js +199 -0
- package/dist/core/openapi/merge/components.js.map +1 -0
- package/dist/core/openapi/merge/error.js +16 -0
- package/dist/core/openapi/merge/error.js.map +1 -0
- package/dist/core/openapi/merge/markers.js +204 -0
- package/dist/core/openapi/merge/markers.js.map +1 -0
- package/dist/core/openapi/merge/merge.js +232 -0
- package/dist/core/openapi/merge/merge.js.map +1 -0
- package/dist/core/openapi/merge/pin.js +110 -0
- package/dist/core/openapi/merge/pin.js.map +1 -0
- package/dist/core/openspec/decide/capabilities.js +244 -0
- package/dist/core/openspec/decide/capabilities.js.map +1 -0
- package/dist/core/openspec/decide/dispositions.js +63 -0
- package/dist/core/openspec/decide/dispositions.js.map +1 -0
- package/dist/core/openspec/decide/renames.js +179 -0
- package/dist/core/openspec/decide/renames.js.map +1 -0
- package/dist/core/openspec/decide/verdict.js +84 -0
- package/dist/core/openspec/decide/verdict.js.map +1 -0
- package/dist/core/openspec/inventory.js +220 -0
- package/dist/core/openspec/inventory.js.map +1 -0
- package/dist/core/openspec/model/mapping.js +78 -0
- package/dist/core/openspec/model/mapping.js.map +1 -0
- package/dist/core/openspec/model/model.js +70 -0
- package/dist/core/openspec/model/model.js.map +1 -0
- package/dist/core/openspec/scan/changes.js +241 -0
- package/dist/core/openspec/scan/changes.js.map +1 -0
- package/dist/core/openspec/scan/config.js +191 -0
- package/dist/core/openspec/scan/config.js.map +1 -0
- package/dist/core/openspec/scan/shape.js +304 -0
- package/dist/core/openspec/scan/shape.js.map +1 -0
- package/dist/core/openspec/scan/walk.js +116 -0
- package/dist/core/openspec/scan/walk.js.map +1 -0
- package/dist/core/openspec/scan/workspace.js +71 -0
- package/dist/core/openspec/scan/workspace.js.map +1 -0
- package/dist/core/owners/codeowners.js +141 -0
- package/dist/core/owners/codeowners.js.map +1 -0
- package/dist/core/pack/features.js +120 -0
- package/dist/core/pack/features.js.map +1 -0
- package/dist/core/pack/joins.js +112 -0
- package/dist/core/pack/joins.js.map +1 -0
- package/dist/core/pack/living.js +157 -0
- package/dist/core/pack/living.js.map +1 -0
- package/dist/core/pack/pack.js +120 -0
- package/dist/core/pack/pack.js.map +1 -0
- package/dist/core/permissions/permissions.js +87 -0
- package/dist/core/permissions/permissions.js.map +1 -0
- package/dist/core/projection/api.js +84 -0
- package/dist/core/projection/api.js.map +1 -0
- package/dist/core/projection/arch-slice.js +63 -0
- package/dist/core/projection/arch-slice.js.map +1 -0
- package/dist/core/projection/events.js +34 -0
- package/dist/core/projection/events.js.map +1 -0
- package/dist/core/projection/introduced.js +14 -0
- package/dist/core/projection/introduced.js.map +1 -0
- package/dist/core/provenance/findings.js +244 -0
- package/dist/core/provenance/findings.js.map +1 -0
- package/dist/core/provenance/git.js +201 -0
- package/dist/core/provenance/git.js.map +1 -0
- package/dist/core/provenance/gitq/moves.js +139 -0
- package/dist/core/provenance/gitq/moves.js.map +1 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
- package/dist/core/provenance/sample/scope.js +238 -0
- package/dist/core/provenance/sample/scope.js.map +1 -0
- package/dist/core/provenance/sample/sections.js +61 -0
- package/dist/core/provenance/sample/sections.js.map +1 -0
- package/dist/core/provenance/sources.js +280 -0
- package/dist/core/provenance/sources.js.map +1 -0
- package/dist/core/provenance/stamp.js +220 -0
- package/dist/core/provenance/stamp.js.map +1 -0
- package/dist/core/provenance/walk.js +157 -0
- package/dist/core/provenance/walk.js.map +1 -0
- package/dist/core/repo/authored/paths.js +210 -0
- package/dist/core/repo/authored/paths.js.map +1 -0
- package/dist/core/repo/entries.js +126 -0
- package/dist/core/repo/entries.js.map +1 -0
- package/dist/core/repo/paths.js +322 -0
- package/dist/core/repo/paths.js.map +1 -0
- package/dist/core/repo/repo.js +308 -0
- package/dist/core/repo/repo.js.map +1 -0
- package/dist/core/repo/service-target.js +153 -0
- package/dist/core/repo/service-target.js.map +1 -0
- package/dist/core/repo/state.js +58 -0
- package/dist/core/repo/state.js.map +1 -0
- package/dist/core/repo/tree/find.js +61 -0
- package/dist/core/repo/tree/find.js.map +1 -0
- package/dist/core/repo/tree/fs.js +90 -0
- package/dist/core/repo/tree/fs.js.map +1 -0
- package/dist/core/repo/tree/marker.js +61 -0
- package/dist/core/repo/tree/marker.js.map +1 -0
- package/dist/core/repo/tree/render/body.js +258 -0
- package/dist/core/repo/tree/render/body.js.map +1 -0
- package/dist/core/repo/tree/render/projects.js +182 -0
- package/dist/core/repo/tree/render/projects.js.map +1 -0
- package/dist/core/repo/tree/render/service/header.js +125 -0
- package/dist/core/repo/tree/render/service/header.js.map +1 -0
- package/dist/core/repo/tree/render/service/peers.js +28 -0
- package/dist/core/repo/tree/render/service/peers.js.map +1 -0
- package/dist/core/repo/tree/render/service/view-id.js +67 -0
- package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
- package/dist/core/repo/tree/render/service/views.js +78 -0
- package/dist/core/repo/tree/render/service/views.js.map +1 -0
- package/dist/core/repo/tree/render/stale.js +42 -0
- package/dist/core/repo/tree/render/stale.js.map +1 -0
- package/dist/core/repo/tree/render/text.js +84 -0
- package/dist/core/repo/tree/render/text.js.map +1 -0
- package/dist/core/repo/tree/render/views.js +293 -0
- package/dist/core/repo/tree/render/views.js.map +1 -0
- package/dist/core/repo/tree/walk.js +255 -0
- package/dist/core/repo/tree/walk.js.map +1 -0
- package/dist/core/results.js +146 -14
- package/dist/core/results.js.map +1 -1
- package/dist/core/scaffold/landscape.js +239 -0
- package/dist/core/scaffold/landscape.js.map +1 -0
- package/dist/core/scaffold/legacy.js +271 -0
- package/dist/core/scaffold/legacy.js.map +1 -0
- package/dist/core/scaffold/readme.js +84 -0
- package/dist/core/scaffold/readme.js.map +1 -0
- package/dist/core/staging/commit.js +252 -0
- package/dist/core/staging/commit.js.map +1 -0
- package/dist/core/staging/interrupted.js +96 -0
- package/dist/core/staging/interrupted.js.map +1 -0
- package/dist/core/staging/lock.js +268 -0
- package/dist/core/staging/lock.js.map +1 -0
- package/dist/core/staging/recovery/finding.js +35 -0
- package/dist/core/staging/recovery/finding.js.map +1 -0
- package/dist/core/staging/recovery/intent.js +95 -0
- package/dist/core/staging/recovery/intent.js.map +1 -0
- package/dist/core/staging/recovery/recover.js +204 -0
- package/dist/core/staging/recovery/recover.js.map +1 -0
- package/dist/core/staging/recovery/residue.js +62 -0
- package/dist/core/staging/recovery/residue.js.map +1 -0
- package/dist/core/staging/snapshot/manifest.js +188 -0
- package/dist/core/staging/snapshot/manifest.js.map +1 -0
- package/dist/core/staging/snapshot/snapshot.js +196 -0
- package/dist/core/staging/snapshot/snapshot.js.map +1 -0
- package/dist/core/staging/txn/forward.js +181 -0
- package/dist/core/staging/txn/forward.js.map +1 -0
- package/dist/core/staging/txn/journal.js +139 -0
- package/dist/core/staging/txn/journal.js.map +1 -0
- package/dist/core/staging/txn/transaction.js +75 -0
- package/dist/core/staging/txn/transaction.js.map +1 -0
- package/dist/core/staging/writes.js +113 -0
- package/dist/core/staging/writes.js.map +1 -0
- package/dist/core/status/actions/execution.js +60 -0
- package/dist/core/status/actions/execution.js.map +1 -0
- package/dist/core/status/contracts.js +126 -0
- package/dist/core/status/contracts.js.map +1 -0
- package/dist/core/status/feature/artifacts.js +196 -0
- package/dist/core/status/feature/artifacts.js.map +1 -0
- package/dist/core/status/feature/feature.js +214 -0
- package/dist/core/status/feature/feature.js.map +1 -0
- package/dist/core/status/feature/next.js +163 -0
- package/dist/core/status/feature/next.js.map +1 -0
- package/dist/core/status/feature/state.js +21 -0
- package/dist/core/status/feature/state.js.map +1 -0
- package/dist/core/status/feature/words.js +96 -0
- package/dist/core/status/feature/words.js.map +1 -0
- package/dist/core/status/fleet/fleet.js +216 -0
- package/dist/core/status/fleet/fleet.js.map +1 -0
- package/dist/core/status/fleet/next.js +302 -0
- package/dist/core/status/fleet/next.js.map +1 -0
- package/dist/core/status/interrupted.js +97 -0
- package/dist/core/status/interrupted.js.map +1 -0
- package/dist/core/status/report.js +31 -0
- package/dist/core/status/report.js.map +1 -0
- package/dist/core/status/scan.js +41 -0
- package/dist/core/status/scan.js.map +1 -0
- package/dist/core/status/verification.js +216 -0
- package/dist/core/status/verification.js.map +1 -0
- package/dist/core/tasks/packet.js +199 -0
- package/dist/core/tasks/packet.js.map +1 -0
- package/dist/core/tasks/record.js +43 -0
- package/dist/core/tasks/record.js.map +1 -0
- package/dist/core/tasks/sources.js +181 -0
- package/dist/core/tasks/sources.js.map +1 -0
- package/dist/core/tasks/stamps.js +76 -0
- package/dist/core/tasks/stamps.js.map +1 -0
- package/dist/core/tasks/unit.js +0 -0
- package/dist/core/tasks/unit.js.map +1 -0
- package/dist/core/usecases/capability.js +166 -0
- package/dist/core/usecases/capability.js.map +1 -0
- package/dist/core/usecases/delta/claims.js +140 -0
- package/dist/core/usecases/delta/claims.js.map +1 -0
- package/dist/core/usecases/delta/flows.js +101 -0
- package/dist/core/usecases/delta/flows.js.map +1 -0
- package/dist/core/usecases/delta/modified.js +315 -0
- package/dist/core/usecases/delta/modified.js.map +1 -0
- package/dist/core/usecases/delta/overlay.js +76 -0
- package/dist/core/usecases/delta/overlay.js.map +1 -0
- package/dist/core/usecases/fleet.js +296 -0
- package/dist/core/usecases/fleet.js.map +1 -0
- package/dist/core/usecases/grade/capability-tag.js +93 -0
- package/dist/core/usecases/grade/capability-tag.js.map +1 -0
- package/dist/core/usecases/grade/feature.js +88 -0
- package/dist/core/usecases/grade/feature.js.map +1 -0
- package/dist/core/usecases/grade/requirement-tag.js +94 -0
- package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
- package/dist/core/usecases/grade/steps.js +266 -0
- package/dist/core/usecases/grade/steps.js.map +1 -0
- package/dist/core/usecases/grade/usecases.js +112 -0
- package/dist/core/usecases/grade/usecases.js.map +1 -0
- package/dist/core/usecases/operations.js +123 -0
- package/dist/core/usecases/operations.js.map +1 -0
- package/dist/core/usecases/place.js +44 -0
- package/dist/core/usecases/place.js.map +1 -0
- package/dist/core/usecases/service/exclude.js +119 -0
- package/dist/core/usecases/service/exclude.js.map +1 -0
- package/dist/core/usecases/service/flows.js +164 -0
- package/dist/core/usecases/service/flows.js.map +1 -0
- package/dist/core/usecases/suites/participants.js +83 -0
- package/dist/core/usecases/suites/participants.js.map +1 -0
- package/dist/core/usecases/suites/suites.js +45 -0
- package/dist/core/usecases/suites/suites.js.map +1 -0
- package/dist/core/usecases/touch.js +101 -0
- package/dist/core/usecases/touch.js.map +1 -0
- package/dist/core/verify/answers.js +180 -0
- package/dist/core/verify/answers.js.map +1 -0
- package/dist/core/verify/build.js +170 -0
- package/dist/core/verify/build.js.map +1 -0
- package/dist/core/verify/checklist.js +259 -0
- package/dist/core/verify/checklist.js.map +1 -0
- package/dist/core/verify/claims/identity.js +61 -0
- package/dist/core/verify/claims/identity.js.map +1 -0
- package/dist/core/verify/cross/diff.js +97 -0
- package/dist/core/verify/cross/diff.js.map +1 -0
- package/dist/core/verify/evidence/contract.js +169 -0
- package/dist/core/verify/evidence/contract.js.map +1 -0
- package/dist/core/verify/location.js +67 -0
- package/dist/core/verify/location.js.map +1 -0
- package/dist/core/verify/pins/lint.js +158 -0
- package/dist/core/verify/pins/lint.js.map +1 -0
- package/dist/core/verify/pins/pin.js +95 -0
- package/dist/core/verify/pins/pin.js.map +1 -0
- package/dist/core/verify/read/file.js +224 -0
- package/dist/core/verify/read/file.js.map +1 -0
- package/dist/core/verify/read/shape.js +169 -0
- package/dist/core/verify/read/shape.js.map +1 -0
- package/dist/core/verify/read/union.js +105 -0
- package/dist/core/verify/read/union.js.map +1 -0
- package/dist/core/verify/record.js +165 -0
- package/dist/core/verify/record.js.map +1 -0
- package/dist/core/verify/store/commit.js +99 -0
- package/dist/core/verify/store/commit.js.map +1 -0
- package/dist/core/verify/store/render.js +61 -0
- package/dist/core/verify/store/render.js.map +1 -0
- package/dist/core/vocabulary/health.js +91 -0
- package/dist/core/vocabulary/health.js.map +1 -0
- package/dist/core/vocabulary/issue.js +59 -0
- package/dist/core/vocabulary/issue.js.map +1 -0
- package/dist/core/vocabulary/maturity.js +216 -0
- package/dist/core/vocabulary/maturity.js.map +1 -0
- package/dist/core/vocabulary/report.js +90 -0
- package/dist/core/vocabulary/report.js.map +1 -0
- package/dist/core/vocabulary/steps.js +266 -0
- package/dist/core/vocabulary/steps.js.map +1 -0
- package/dist/core/workspace/discover.js +239 -0
- package/dist/core/workspace/discover.js.map +1 -0
- package/dist/core/workspace/render.js +52 -0
- package/dist/core/workspace/render.js.map +1 -0
- package/examples/README.md +232 -0
- package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
- package/examples/docs/architecture/capabilities.yaml +31 -0
- package/examples/docs/architecture/landscape.likec4 +336 -0
- package/examples/docs/architecture/obligations.yaml +23 -0
- package/examples/docs/architecture/permissions.yaml +50 -0
- package/examples/docs/architecture/services.likec4 +70 -0
- package/examples/docs/architecture/subsystems.likec4 +18 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +74 -0
- package/examples/docs/capabilities/checkout/spec.md +55 -0
- package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
- package/examples/docs/capabilities/order-notifications/spec.md +43 -0
- package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
- package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
- package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +132 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +31 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
- package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
- package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +73 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
- package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
- package/examples/docs/glossary/order.md +15 -0
- package/examples/docs/glossary/payments/authorization.md +14 -0
- package/examples/docs/likec4.config.json +8 -0
- package/examples/docs/loam.json +3 -0
- package/examples/docs/services/checkout-web/model.likec4 +92 -0
- package/examples/docs/services/checkout-web/spec.md +91 -0
- package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
- package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
- package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
- package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
- package/examples/docs/services/order-service/arch.spec.md +134 -0
- package/examples/docs/services/order-service/asyncapi.yaml +109 -0
- package/examples/docs/services/order-service/health.yaml +57 -0
- package/examples/docs/services/order-service/model.likec4 +126 -0
- package/examples/docs/services/order-service/openapi.yaml +257 -0
- package/examples/docs/services/order-service/runbook.md +62 -0
- package/examples/docs/services/order-service/spec.md +134 -0
- package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
- package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
- package/examples/docs/services/payment-service/arch.spec.md +76 -0
- package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
- package/examples/docs/services/payment-service/health.yaml +39 -0
- package/examples/docs/services/payment-service/model.likec4 +68 -0
- package/examples/docs/services/payment-service/openapi.yaml +151 -0
- package/examples/docs/services/payment-service/runbook.md +37 -0
- package/examples/docs/services/payment-service/spec.md +88 -0
- package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
- package/examples/docs/services/platform/identity-service/arch.spec.md +120 -0
- package/examples/docs/services/platform/identity-service/health.yaml +70 -0
- package/examples/docs/services/platform/identity-service/model.likec4 +81 -0
- package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
- package/examples/docs/services/platform/identity-service/runbook.md +95 -0
- package/examples/docs/services/platform/identity-service/spec.md +119 -0
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +200 -0
- package/examples/docs/services/platform/notification-service/health.yaml +40 -0
- package/examples/docs/services/platform/notification-service/model.likec4 +97 -0
- package/examples/docs/services/platform/notification-service/runbook.md +48 -0
- package/examples/docs/services/platform/notification-service/spec.md +112 -0
- package/examples/docs/services/platform/subsystem.yaml +2 -0
- package/examples/fleet.yaml +57 -0
- package/package.json +19 -6
- package/dist/commands/adopt.js +0 -240
- package/dist/commands/adopt.js.map +0 -1
- package/dist/commands/archive.js +0 -1313
- package/dist/commands/archive.js.map +0 -1
- package/dist/commands/delta.js +0 -388
- package/dist/commands/delta.js.map +0 -1
- package/dist/commands/docs-repo-gate.js.map +0 -1
- package/dist/commands/format.js +0 -17
- package/dist/commands/format.js.map +0 -1
- package/dist/commands/gherkin.js +0 -309
- package/dist/commands/gherkin.js.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/list.js +0 -295
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/migrate-openspec.js +0 -950
- package/dist/commands/migrate-openspec.js.map +0 -1
- package/dist/commands/new.js +0 -413
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/rebase.js +0 -356
- package/dist/commands/rebase.js.map +0 -1
- package/dist/commands/show.js +0 -320
- package/dist/commands/show.js.map +0 -1
- package/dist/commands/status.js +0 -262
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/unarchive.js +0 -317
- package/dist/commands/unarchive.js.map +0 -1
- package/dist/commands/validate.js +0 -1773
- package/dist/commands/validate.js.map +0 -1
- package/dist/commands/verify.js +0 -775
- package/dist/commands/verify.js.map +0 -1
- package/dist/commands/vouch.js +0 -421
- package/dist/commands/vouch.js.map +0 -1
- package/dist/core/agent.js +0 -2173
- package/dist/core/agent.js.map +0 -1
- package/dist/core/agents-stamp.js.map +0 -1
- package/dist/core/arch.js +0 -130
- package/dist/core/arch.js.map +0 -1
- package/dist/core/asyncapi.js +0 -253
- package/dist/core/asyncapi.js.map +0 -1
- package/dist/core/brief.js +0 -755
- package/dist/core/brief.js.map +0 -1
- package/dist/core/coherence.js +0 -577
- package/dist/core/coherence.js.map +0 -1
- package/dist/core/concurrency.js.map +0 -1
- package/dist/core/config.js.map +0 -1
- package/dist/core/delta.js +0 -476
- package/dist/core/delta.js.map +0 -1
- package/dist/core/dependencies.js +0 -356
- package/dist/core/dependencies.js.map +0 -1
- package/dist/core/doctor.js +0 -596
- package/dist/core/doctor.js.map +0 -1
- package/dist/core/document-bytes.js.map +0 -1
- package/dist/core/explore.js +0 -225
- package/dist/core/explore.js.map +0 -1
- package/dist/core/frontmatter.js.map +0 -1
- package/dist/core/gherkin.js +0 -463
- package/dist/core/gherkin.js.map +0 -1
- package/dist/core/health.js +0 -67
- package/dist/core/health.js.map +0 -1
- package/dist/core/ids.js +0 -122
- package/dist/core/ids.js.map +0 -1
- package/dist/core/issue.js +0 -9
- package/dist/core/issue.js.map +0 -1
- package/dist/core/json.js +0 -74
- package/dist/core/json.js.map +0 -1
- package/dist/core/likec4.js +0 -489
- package/dist/core/likec4.js.map +0 -1
- package/dist/core/maturity.js +0 -66
- package/dist/core/maturity.js.map +0 -1
- package/dist/core/openapi-merge.js +0 -552
- package/dist/core/openapi-merge.js.map +0 -1
- package/dist/core/openapi.js.map +0 -1
- package/dist/core/openspec-inventory.js +0 -1440
- package/dist/core/openspec-inventory.js.map +0 -1
- package/dist/core/path-safety.js.map +0 -1
- package/dist/core/provenance.js +0 -787
- package/dist/core/provenance.js.map +0 -1
- package/dist/core/records.js.map +0 -1
- package/dist/core/repo.js +0 -438
- package/dist/core/repo.js.map +0 -1
- package/dist/core/report.js +0 -47
- package/dist/core/report.js.map +0 -1
- package/dist/core/spec.js +0 -505
- package/dist/core/spec.js.map +0 -1
- package/dist/core/staging.js +0 -890
- package/dist/core/staging.js.map +0 -1
- package/dist/core/status.js +0 -1132
- package/dist/core/status.js.map +0 -1
- package/dist/core/steps.js +0 -72
- package/dist/core/steps.js.map +0 -1
- package/dist/core/verify.js +0 -807
- package/dist/core/verify.js.map +0 -1
- package/dist/core/version.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit.js","sourceRoot":"","sources":["../../../src/core/gherkin/emit.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A requirement set as a `.feature` file: what gets written where, and what the\n * text says.\n *\n * The plan is separate from the render because the two answer to different\n * owners — where a suite lands is the service repository's layout question\n * (`./stamp.ts` owns the root), while what a scenario reads like is the\n * requirement's. Every emitted scenario carries the digest tag the claim\n * checklist matches on, which is why nothing here may reword a scenario body.\n */\nimport { type Requirement } from \"../document/spec.js\";\nimport { scenarioGherkin } from \"../vocabulary/steps.js\";\nimport { type SpecAxis } from \"../repo/paths.js\";\nimport { type ScenarioAxis } from \"./digest.js\";\nimport { gherkinStampLine, scenarioDigest, scenarioDigestTag } from \"./stamp.js\";\n\n/** The axis label the machine contract speaks. */\nexport function axisLabel(axis: SpecAxis): \"business\" | \"arch\" {\n return axis.key === \"archSpec\" ? \"arch\" : \"business\";\n}\n\n/**\n * A requirement name as a file name: lowercase, every non-letter/digit run one\n * hyphen, edges trimmed.\n *\n * The keep-set is `\\p{L}\\p{N}`, not `[a-z0-9]`, because an ASCII-only slug is\n * not a slug of a non-Latin fleet — it is the empty string. Every requirement\n * written in Chinese, Japanese, Korean, Greek, Cyrillic or Hebrew slugged to\n * nothing, fell back to `requirement`, and the whole service's suite came out\n * as `requirement.feature`, `requirement-2.feature`, … : file names that name\n * nothing, in the one place the mapping is supposed to be legible. So letters\n * of every script survive as themselves.\n *\n * Accents are folded rather than kept, and that is the reason for NFKD first:\n * decomposing splits `é` into `e` + a combining acute, and dropping the\n * combining marks (`\\p{M}`) leaves `e`. Without the fold `émojis` and `emojis`\n * would be two files that look identical in a directory listing — and on a\n * filesystem that normalises names (APFS, and HFS+ before it) they would be one\n * file, silently, with the second emission overwriting the first.\n *\n * A name this still leaves empty (all punctuation or emoji) falls back to\n * \"requirement\" and lets the collision counter number it. Runs collapse to a\n * SINGLE hyphen, which is what keeps the `arch--` prefix an axis marker no\n * business slug can spell.\n */\nexport function slugOf(name: string): string {\n const s = name\n .normalize(\"NFKD\")\n .replace(/\\p{M}+/gu, \"\")\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return s.length > 0 ? s : \"requirement\";\n}\n\nexport interface PlannedFeature {\n /** File name under `<gherkinDir>/loam/` — flat, never a subdirectory. */\n fileName: string;\n axis: SpecAxis;\n requirement: Requirement;\n /** One digest per scenario, in scenario order — what the stamps say. */\n digests: string[];\n /**\n * Names of scenarios that yielded ZERO steps — numbered-step or prose-only\n * legacy bodies. Their `Scenario:` blocks are description-only: cucumber\n * runs them vacuously green, while `--results` (which requires at least one\n * passed step) can never confirm them — so the emission must say so out\n * loud, and the fix is rewording the spec bullets, never editing the file.\n */\n stepless: string[];\n /**\n * Names of scenarios whose markdown table could not be read as `Examples` —\n * see `ScenarioGherkin.malformedExamples`. The table stays in the description\n * and the file is valid Gherkin, so nothing downstream can notice: the\n * emission is the only place this is visible at all.\n */\n malformedExamples: string[];\n /**\n * Names of scenarios that wrote a fenced block or an indented table where it\n * could not become a step argument — see `ScenarioGherkin.strandedBlocks`.\n * The text survives as description, so the file is valid Gherkin and the\n * document still reads correctly; what is gone is the ARGUMENT, and a step\n * definition that expected a payload or a table receives nothing.\n */\n strandedBlocks: string[];\n content: string;\n}\n\n/**\n * The emission plan for one scope: one `.feature` per requirement.\n *\n * File naming is the mapping's contract, so it is spelled here once:\n * `<slug>.feature` for the business axis, `arch--<slug>.feature` for the\n * architecture axis — flat and diffable, and collision-proof across axes\n * because slugs collapse punctuation runs to a single hyphen, so no business\n * slug can ever begin `arch--`. Two requirements slugging identically are\n * disambiguated in document order: the first keeps the bare slug, later ones\n * take `-2`, `-3`, … (counting up past any name that already owns the suffix).\n *\n * Tags ride the `Feature:` line and are inherited by every scenario in the\n * file: `@<FEAT>` in feature mode (living emissions carry no feature tag),\n * `@architecture` on the arch axis.\n *\n * `opts.service` is the suite's owner and salts every digest stamp — the same\n * service `loam verify` files the matching claims under. Required, not\n * defaulted: an emission that guessed it would stamp tags no claim matches.\n */\nexport function planEmission(\n byAxis: Array<{ axis: SpecAxis; reqs: Requirement[] }>,\n opts: { service: string; featureTag?: string; version: string },\n): PlannedFeature[] {\n const used = new Set<string>();\n const out: PlannedFeature[] = [];\n for (const { axis, reqs } of byAxis) {\n for (const r of reqs) {\n const base = (axis.key === \"archSpec\" ? \"arch--\" : \"\") + slugOf(r.name);\n let fileName = `${base}.feature`;\n for (let n = 2; used.has(fileName); n += 1) fileName = `${base}-${n}.feature`;\n used.add(fileName);\n const tags = [\n ...(opts.featureTag === undefined ? [] : [`@${opts.featureTag}`]),\n ...(axis.key === \"archSpec\" ? [\"@architecture\"] : []),\n ];\n const { content, digests, stepless, malformedExamples, strandedBlocks } = renderFeature(\n r,\n { tags, version: opts.version },\n opts.service,\n axisLabel(axis),\n );\n out.push({ fileName, axis, requirement: r, digests, stepless, malformedExamples, strandedBlocks, content });\n }\n }\n return out;\n}\n\n/**\n * One requirement as one `.feature` file: `Feature:` is the requirement name,\n * its body text (Operations:/Covers: lines included — they are part of what\n * the requirement says) becomes the feature description, and each scenario\n * renders under its `@loam-digest-…` tag — the tag line sits directly above\n * the `Scenario:` keyword, where Gherkin puts scenario-level tags, so the\n * runner attaches it to the scenario and carries it into the JSON report.\n * Deterministic to the byte.\n *\n * `service` is the owner of the suite being written, and it salts every digest\n * — the same service `loam verify` files the matching claims under. The two\n * have to be the same string or the stamps this writes match no claim at all.\n */\n/** What the emitted file is stamped with, beside the requirement it renders. */\nexport interface Stamp {\n tags: string[];\n version: string;\n}\n\nexport function renderFeature(\n r: Requirement,\n stamp: Stamp,\n service: string,\n axis: ScenarioAxis = \"business\",\n): { content: string; digests: string[]; stepless: string[]; malformedExamples: string[]; strandedBlocks: string[] } {\n const { tags, version } = stamp;\n const lines: string[] = [gherkinStampLine(version)];\n if (tags.length > 0) lines.push(tags.join(\" \"));\n lines.push(`Feature: ${r.name}`);\n const text = r.text.join(\"\\n\").trim();\n if (text.length > 0) {\n lines.push(\"\");\n for (const l of text.split(\"\\n\")) lines.push(l.length > 0 ? ` ${l}` : \"\");\n }\n const digests: string[] = [];\n const stepless: string[] = [];\n const malformedExamples: string[] = [];\n const strandedBlocks: string[] = [];\n for (const s of r.scenarios) {\n // The digest hashes the WHOLE body, table rows included, so a changed cell\n // is a changed scenario: `gherkin.stale` fires on an edited case exactly as\n // it does on an edited step, and the claim it answers moves with it. It is\n // also why a change to THIS renderer moves no digest: the hash is over the\n // markdown source, so a suite generated before a grammar change keeps its\n // stamps and grades `gherkin.current` until somebody regenerates it.\n const digest = scenarioDigest(service, s.lines, axis);\n digests.push(digest);\n const { description, steps, examples, malformedExamples: broken, strandedBlocks: stranded } = scenarioGherkin(s.lines);\n if (steps.length === 0) stepless.push(s.name);\n if (broken) malformedExamples.push(s.name);\n if (stranded) strandedBlocks.push(s.name);\n // The keyword is the whole difference between one case and twenty: cucumber\n // expands an outline into one report element per row, each carrying this\n // scenario's tag, and `runnerAnswers` confirms the claim only when every\n // one of them passed.\n lines.push(\"\", ` ${scenarioDigestTag(digest)}`, ` ${examples === null ? \"Scenario\" : \"Scenario Outline\"}: ${s.name}`);\n for (const d of description) lines.push(` ${d}`);\n for (const st of steps) {\n lines.push(` ${st.text}`);\n // Gherkin puts a step's argument UNDER the step, indented past it, and\n // permits at most one of each kind. Docstring first, so a step carrying\n // both reads request-then-expectation.\n if (st.docstring !== undefined) {\n lines.push(` \"\"\"${st.docstring.contentType ?? \"\"}`);\n // A payload containing its own `\"\"\"` would close the block early and\n // silently move the rest of the request body into the step list.\n for (const l of st.docstring.lines) lines.push(l.length === 0 ? \"\" : ` ${l.replace(/\"\"\"/g, '\\\\\"\\\\\"\\\\\"')}`);\n lines.push(' \"\"\"');\n }\n if (st.table !== undefined) for (const row of st.table) lines.push(` ${tableRow(row, st.table)}`);\n }\n if (examples !== null) {\n lines.push(\"\", \" Examples:\");\n for (const row of [examples.header, ...examples.rows]) lines.push(` ${tableRow(row, [examples.header, ...examples.rows])}`);\n }\n }\n return { content: lines.join(\"\\n\") + \"\\n\", digests, stepless, malformedExamples, strandedBlocks };\n}\n\n/**\n * One table row — an `Examples` case or a step's data table — with columns\n * padded to the widest cell in that table.\n *\n * Padding is cosmetic to cucumber and deliberate here: these files are read in\n * review as often as they are run, and an unaligned twenty-row matrix is where\n * a wrong cell hides. Deterministic to the byte, like everything else the\n * emitter writes — the widths come from the table alone.\n *\n * ESCAPING IS NOT COSMETIC. Gherkin's table grammar ends a cell at `|`, so a\n * cell containing one silently becomes two cells and every row after it\n * disagrees with the header; `\\` and a literal newline are the same class. The\n * escapes go on before the padding is measured, because the emitted width is\n * the escaped width — measuring the raw cell produced columns that drifted by\n * one character per escape.\n */\nfunction tableRow(row: string[], all: readonly string[][]): string {\n const esc = (c: string): string => c.replace(/\\\\/g, \"\\\\\\\\\").replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \"\\\\n\");\n const widths = row.map((_, i) => Math.max(...all.map((r) => esc(r[i] ?? \"\").length)));\n return `| ${row.map((c, i) => esc(c).padEnd(widths[i]!)).join(\" | \")} |`;\n}\n"]}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A generated `.feature` file read back — the stamp, the tags, the scenarios.
|
|
3
|
+
*
|
|
4
|
+
* The parser is deliberately shallow and forgiving in one direction only: a file
|
|
5
|
+
* without loam's stamp is not loam's to grade, and returns null rather than a
|
|
6
|
+
* partially understood suite. Anything that IS stamped is loam's output, so the
|
|
7
|
+
* digests inside it are trusted to be the ones `./emit.ts` wrote.
|
|
8
|
+
*/
|
|
9
|
+
import { DIGEST_TAG_RE, STAMP_RE } from "./stamp.js";
|
|
10
|
+
/**
|
|
11
|
+
* Parse a `.feature` file for its loam stamps. Null when line 1 is not the
|
|
12
|
+
* stamp — an unstamped file is not loam's writing, and no staleness judgment
|
|
13
|
+
* may be built on it. Tag lines above `Feature:` are the file's tags; a tag
|
|
14
|
+
* line after it that carries a `@loam-digest-…` token binds that digest to the
|
|
15
|
+
* immediately following `Scenario:` line (tag lines stack, as Gherkin's do,
|
|
16
|
+
* without breaking the bond); any other non-blank line between them breaks it.
|
|
17
|
+
*
|
|
18
|
+
* DOCSTRING BODIES ARE SKIPPED WHOLE, and that is a correctness requirement
|
|
19
|
+
* rather than tidiness. Since a step may carry a `"""` payload, a request body
|
|
20
|
+
* containing a line that reads `Scenario: …` or begins with `@` would otherwise
|
|
21
|
+
* be read here as a real scenario or a real tag — and every staleness verdict
|
|
22
|
+
* (`gherkin.stale`, `gherkin.missing`, `gherkin.orphaned`) plus the tag that
|
|
23
|
+
* decides whether an in-flight feature's file may be replaced rests on this
|
|
24
|
+
* parse. The payload would be quietly deciding what the suite promises.
|
|
25
|
+
*/
|
|
26
|
+
export function parseStampedFeature(text) {
|
|
27
|
+
const lines = text.split(/\r?\n/);
|
|
28
|
+
const m = STAMP_RE.exec(lines[0] ?? "");
|
|
29
|
+
if (!m)
|
|
30
|
+
return null;
|
|
31
|
+
const tags = [];
|
|
32
|
+
let featureName = null;
|
|
33
|
+
const scenarios = [];
|
|
34
|
+
let pending = null;
|
|
35
|
+
let inDocString = false;
|
|
36
|
+
for (const line of lines.slice(1)) {
|
|
37
|
+
// `"""` opens and closes; nothing between the two is structure. The emitter
|
|
38
|
+
// escapes an inner `"""` as `\"\"\"`, so an unescaped one here is always a
|
|
39
|
+
// real delimiter.
|
|
40
|
+
if (/^\s*"""/.test(line)) {
|
|
41
|
+
inDocString = !inDocString;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (inDocString)
|
|
45
|
+
continue;
|
|
46
|
+
if (/^\s*@/.test(line)) {
|
|
47
|
+
for (const t of line.trim().split(/\s+/)) {
|
|
48
|
+
if (featureName === null) {
|
|
49
|
+
if (t.startsWith("@"))
|
|
50
|
+
tags.push(t.slice(1));
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const d = DIGEST_TAG_RE.exec(t);
|
|
54
|
+
if (d)
|
|
55
|
+
pending = d[1];
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const f = /^\s*Feature:\s*(.*)$/.exec(line);
|
|
60
|
+
if (f && featureName === null) {
|
|
61
|
+
featureName = f[1].trim();
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const s = /^\s*Scenario(?: Outline)?:\s*(.*)$/.exec(line);
|
|
65
|
+
if (s) {
|
|
66
|
+
if (pending !== null)
|
|
67
|
+
scenarios.push({ name: s[1].trim(), digest: pending });
|
|
68
|
+
pending = null;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (pending !== null && line.trim().length > 0)
|
|
72
|
+
pending = null;
|
|
73
|
+
}
|
|
74
|
+
return { version: m[1], tags, featureName, scenarios };
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/core/gherkin/read.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAmBrD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACxC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,WAAW,GAAkB,IAAI,CAAC;IACtC,MAAM,SAAS,GAAsB,EAAE,CAAC;IACxC,IAAI,OAAO,GAAkB,IAAI,CAAC;IAClC,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAClC,4EAA4E;QAC5E,2EAA2E;QAC3E,kBAAkB;QAClB,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,WAAW,GAAG,CAAC,WAAW,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,IAAI,WAAW;YAAE,SAAS;QAC1B,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzC,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;oBACzB,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;wBAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC7C,SAAS;gBACX,CAAC;gBACD,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAChC,IAAI,CAAC;oBAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;YACzB,CAAC;YACD,SAAS;QACX,CAAC;QACD,MAAM,CAAC,GAAG,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YAC9B,WAAW,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,MAAM,CAAC,GAAG,oCAAoC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,IAAI,CAAC,EAAE,CAAC;YACN,IAAI,OAAO,KAAK,IAAI;gBAAE,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;YAC9E,OAAO,GAAG,IAAI,CAAC;YACf,SAAS;QACX,CAAC;QACD,IAAI,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,GAAG,IAAI,CAAC;IACjE,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAE,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;AAC1D,CAAC","sourcesContent":["/**\n * A generated `.feature` file read back — the stamp, the tags, the scenarios.\n *\n * The parser is deliberately shallow and forgiving in one direction only: a file\n * without loam's stamp is not loam's to grade, and returns null rather than a\n * partially understood suite. Anything that IS stamped is loam's output, so the\n * digests inside it are trusted to be the ones `./emit.ts` wrote.\n */\nimport { DIGEST_TAG_RE, STAMP_RE } from \"./stamp.js\";\n\nexport interface StampedScenario {\n name: string;\n digest: string;\n}\n\n/** A generated file as the staleness checks see it. */\nexport interface StampedFeature {\n /** The version in the stamp line — which loam wrote the file. */\n version: string;\n /** Tag tokens (without `@`) from tag lines above `Feature:`. */\n tags: string[];\n /** The `Feature:` line's text — the requirement name, by the mapping. */\n featureName: string | null;\n /** Scenarios carrying a `@loam-digest-…` tag. A hand-added scenario has none and is invisible here. */\n scenarios: StampedScenario[];\n}\n\n/**\n * Parse a `.feature` file for its loam stamps. Null when line 1 is not the\n * stamp — an unstamped file is not loam's writing, and no staleness judgment\n * may be built on it. Tag lines above `Feature:` are the file's tags; a tag\n * line after it that carries a `@loam-digest-…` token binds that digest to the\n * immediately following `Scenario:` line (tag lines stack, as Gherkin's do,\n * without breaking the bond); any other non-blank line between them breaks it.\n *\n * DOCSTRING BODIES ARE SKIPPED WHOLE, and that is a correctness requirement\n * rather than tidiness. Since a step may carry a `\"\"\"` payload, a request body\n * containing a line that reads `Scenario: …` or begins with `@` would otherwise\n * be read here as a real scenario or a real tag — and every staleness verdict\n * (`gherkin.stale`, `gherkin.missing`, `gherkin.orphaned`) plus the tag that\n * decides whether an in-flight feature's file may be replaced rests on this\n * parse. The payload would be quietly deciding what the suite promises.\n */\nexport function parseStampedFeature(text: string): StampedFeature | null {\n const lines = text.split(/\\r?\\n/);\n const m = STAMP_RE.exec(lines[0] ?? \"\");\n if (!m) return null;\n const tags: string[] = [];\n let featureName: string | null = null;\n const scenarios: StampedScenario[] = [];\n let pending: string | null = null;\n let inDocString = false;\n for (const line of lines.slice(1)) {\n // `\"\"\"` opens and closes; nothing between the two is structure. The emitter\n // escapes an inner `\"\"\"` as `\\\"\\\"\\\"`, so an unescaped one here is always a\n // real delimiter.\n if (/^\\s*\"\"\"/.test(line)) {\n inDocString = !inDocString;\n continue;\n }\n if (inDocString) continue;\n if (/^\\s*@/.test(line)) {\n for (const t of line.trim().split(/\\s+/)) {\n if (featureName === null) {\n if (t.startsWith(\"@\")) tags.push(t.slice(1));\n continue;\n }\n const d = DIGEST_TAG_RE.exec(t);\n if (d) pending = d[1]!;\n }\n continue;\n }\n const f = /^\\s*Feature:\\s*(.*)$/.exec(line);\n if (f && featureName === null) {\n featureName = f[1]!.trim();\n continue;\n }\n const s = /^\\s*Scenario(?: Outline)?:\\s*(.*)$/.exec(line);\n if (s) {\n if (pending !== null) scenarios.push({ name: s[1]!.trim(), digest: pending });\n pending = null;\n continue;\n }\n if (pending !== null && line.trim().length > 0) pending = null;\n }\n return { version: m[1]!, tags, featureName, scenarios };\n}\n"]}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `loam validate` reports about a generated suite: which scenarios moved
|
|
3
|
+
* out from under it, and which were never generated at all.
|
|
4
|
+
*
|
|
5
|
+
* Service-repo-scoped, and silent until a suite exists — a service that never
|
|
6
|
+
* ran `loam gherkin` has not opted into the axis and is not failing it. The
|
|
7
|
+
* whole comparison is by digest rather than by name, so a reworded scenario
|
|
8
|
+
* reads as stale rather than as missing plus new.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync } from "node:fs";
|
|
11
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
12
|
+
import { join, relative, sep } from "node:path";
|
|
13
|
+
import { SPEC_AXES } from "../repo/paths.js";
|
|
14
|
+
import { locateServicePaths } from "../repo/service-target.js";
|
|
15
|
+
import { listFeatures } from "../repo/repo.js";
|
|
16
|
+
import { entryIs, markDescended } from "../repo/tree/fs.js";
|
|
17
|
+
import { UnsafePathError } from "../kernel/path-safety.js";
|
|
18
|
+
import { gherkinRoot, scenarioDigest } from "./stamp.js";
|
|
19
|
+
import { axisLabel } from "./emit.js";
|
|
20
|
+
import { parseStampedFeature } from "./read.js";
|
|
21
|
+
/**
|
|
22
|
+
* The gherkin freshness chain, service-repo-scoped like `sources.*`: it needs
|
|
23
|
+
* the repo (the suite lives there), and it stays quiet until
|
|
24
|
+
* `<gherkinDir>/loam/` exists — a service that never generated has not opted
|
|
25
|
+
* in, and silence there is honest, not vacuous.
|
|
26
|
+
*
|
|
27
|
+
* The LIVING specs are the reference, with one carve-out. A stamped file
|
|
28
|
+
* tagged with a feature still in flight answers to that feature's delta, not
|
|
29
|
+
* to the living spec its requirements have not merged into yet — so it is
|
|
30
|
+
* skipped here (its regeneration is `loam gherkin <FEAT>`), and the moment the
|
|
31
|
+
* feature archives, its requirements ARE living requirements, digests
|
|
32
|
+
* unchanged, and the same file grades current with no rewrite. A tag naming
|
|
33
|
+
* no active feature (the feature was archived, or abandoned) gets no
|
|
34
|
+
* exemption: the living spec is all there is to answer to.
|
|
35
|
+
*
|
|
36
|
+
* Three warnings, one per way the suite and the spec can disagree, judged
|
|
37
|
+
* digest-first and by requirement name second:
|
|
38
|
+
*
|
|
39
|
+
* - `gherkin.missing` — a living scenario whose digest no stamped scenario of
|
|
40
|
+
* its axis carries: the suite has no test for these words.
|
|
41
|
+
* - `gherkin.stale` — a stamped scenario whose digest matches no living
|
|
42
|
+
* scenario while its file's requirement still exists: the spec moved under
|
|
43
|
+
* the generated file. (A reworded scenario therefore reports both: the old
|
|
44
|
+
* words are stale, the new words are missing — one regeneration clears
|
|
45
|
+
* both.)
|
|
46
|
+
* - `gherkin.orphaned` — a whole file whose requirement no longer exists in
|
|
47
|
+
* its axis's living spec; reported once per file, because every scenario in
|
|
48
|
+
* it is moot together.
|
|
49
|
+
*
|
|
50
|
+
* Digests are content identity within one service and one axis, deliberately:
|
|
51
|
+
* two identically-worded scenarios of one service's one axis share one digest,
|
|
52
|
+
* so one stamped copy covers both — while the axes stay separate namespaces
|
|
53
|
+
* twice over (every comparison here is axis-scoped, and the recipe salts the
|
|
54
|
+
* arch axis), and the services stay separate because the recipe salts the
|
|
55
|
+
* service too. That last salt is why a stamped file copied out of another
|
|
56
|
+
* service's repository grades `gherkin.orphaned`/`gherkin.stale` here instead
|
|
57
|
+
* of quietly counting as this service's coverage.
|
|
58
|
+
*
|
|
59
|
+
* All warnings — `--strict` is the CI escalation — plus one `ok` confirmation
|
|
60
|
+
* (`gherkin.current`, the `sources.current` pattern) when a generated suite
|
|
61
|
+
* exists and nothing disagrees.
|
|
62
|
+
*/
|
|
63
|
+
export async function gherkinFindings(ctx) {
|
|
64
|
+
if (ctx.repoDir === undefined)
|
|
65
|
+
return [];
|
|
66
|
+
let root;
|
|
67
|
+
try {
|
|
68
|
+
root = gherkinRoot(ctx.repoDir, ctx.gherkinDir);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
if (!(err instanceof UnsafePathError))
|
|
72
|
+
throw err;
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
severity: "error",
|
|
76
|
+
code: "gherkin.path-outside",
|
|
77
|
+
subject: ctx.service,
|
|
78
|
+
message: `The configured Gherkin output is unsafe: ${err.message}. It must stay inside the service repo.`,
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
if (!existsSync(root))
|
|
83
|
+
return [];
|
|
84
|
+
const paths = await locateServicePaths(ctx.docsDir, ctx.service, ctx.fleet);
|
|
85
|
+
const axes = new Map();
|
|
86
|
+
for (const axis of SPEC_AXES) {
|
|
87
|
+
const label = axisLabel(axis);
|
|
88
|
+
const state = { file: axis.file, reqNames: new Set(), digests: new Set(), scenarios: [] };
|
|
89
|
+
const path = paths[axis.key];
|
|
90
|
+
if (existsSync(path)) {
|
|
91
|
+
for (const r of await ctx.fleet.readRequirements(path)) {
|
|
92
|
+
if (r.kind === "REMOVED")
|
|
93
|
+
continue;
|
|
94
|
+
state.reqNames.add(r.name);
|
|
95
|
+
for (const s of r.scenarios) {
|
|
96
|
+
const digest = scenarioDigest(ctx.service, s.lines, label);
|
|
97
|
+
state.digests.add(digest);
|
|
98
|
+
state.scenarios.push({ req: r.name, name: s.name, digest });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
axes.set(label, state);
|
|
103
|
+
}
|
|
104
|
+
// The stamped side. Every stamped digest counts toward coverage — an
|
|
105
|
+
// in-flight file whose scenario matches the living words IS a test for them.
|
|
106
|
+
// `Set<string>`, not the ids' own brand: this set answers membership
|
|
107
|
+
// questions for tags read out of .feature files — document text.
|
|
108
|
+
const active = new Set((await listFeatures(ctx.docsDir, {}, ctx.fleet)).map((f) => f.id));
|
|
109
|
+
const stampedDigests = { business: new Set(), arch: new Set() };
|
|
110
|
+
const parsed = [];
|
|
111
|
+
let stampedScenarios = 0;
|
|
112
|
+
for (const abs of await featureFilesUnder(root)) {
|
|
113
|
+
const file = parseStampedFeature(await readFile(abs, "utf8"));
|
|
114
|
+
if (file === null)
|
|
115
|
+
continue;
|
|
116
|
+
const axis = file.tags.includes("architecture") ? "arch" : "business";
|
|
117
|
+
for (const s of file.scenarios)
|
|
118
|
+
stampedDigests[axis].add(s.digest);
|
|
119
|
+
stampedScenarios += file.scenarios.length;
|
|
120
|
+
parsed.push({ rel: relative(ctx.repoDir, abs).split(sep).join("/"), axis, file });
|
|
121
|
+
}
|
|
122
|
+
const findings = [];
|
|
123
|
+
const regen = `\`loam gherkin --service ${ctx.service}\``;
|
|
124
|
+
for (const [axis, state] of axes) {
|
|
125
|
+
for (const sc of state.scenarios) {
|
|
126
|
+
if (stampedDigests[axis].has(sc.digest))
|
|
127
|
+
continue;
|
|
128
|
+
findings.push({
|
|
129
|
+
severity: "warn",
|
|
130
|
+
code: "gherkin.missing",
|
|
131
|
+
subject: ctx.service,
|
|
132
|
+
message: `${ctx.service}: scenario '${sc.name}' of requirement '${sc.req}' (${state.file}) has no generated .feature scenario — the suite has no test for these words; ${regen} regenerates it`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
for (const { rel, axis, file } of parsed) {
|
|
137
|
+
if (file.tags.some((t) => active.has(t)))
|
|
138
|
+
continue;
|
|
139
|
+
const state = axes.get(axis);
|
|
140
|
+
if (file.featureName === null || !state.reqNames.has(file.featureName)) {
|
|
141
|
+
findings.push({
|
|
142
|
+
severity: "warn",
|
|
143
|
+
code: "gherkin.orphaned",
|
|
144
|
+
subject: ctx.service,
|
|
145
|
+
message: `${ctx.service}: ${rel}: requirement '${file.featureName ?? "(unnamed)"}' no longer exists in the living ${state.file} — the whole file is orphaned; ${regen} removes it`,
|
|
146
|
+
});
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
for (const s of file.scenarios) {
|
|
150
|
+
if (state.digests.has(s.digest))
|
|
151
|
+
continue;
|
|
152
|
+
findings.push({
|
|
153
|
+
severity: "warn",
|
|
154
|
+
code: "gherkin.stale",
|
|
155
|
+
subject: ctx.service,
|
|
156
|
+
message: `${ctx.service}: ${rel}: scenario '${s.name}' matches no scenario in the living ${state.file} — the spec moved under the generated file; ${regen} regenerates it`,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (findings.length === 0) {
|
|
161
|
+
findings.push({
|
|
162
|
+
severity: "ok",
|
|
163
|
+
code: "gherkin.current",
|
|
164
|
+
message: `${ctx.service}: generated gherkin current (${parsed.length} file(s), ${stampedScenarios} stamped scenario(s) agree with the living specs)`,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
return findings;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Every `.feature` file at or under `root`, sorted by repo-relative path —
|
|
171
|
+
* enumeration is part of the output contract, so the order cannot depend on
|
|
172
|
+
* the filesystem's. Dot-entries are skipped, like every other walk in loam.
|
|
173
|
+
*
|
|
174
|
+
* Symlinks are FOLLOWED, through `repo/tree/fs.ts`'s `entryIs` — the rule
|
|
175
|
+
* every enumeration shares (brief/brief.ts's `hasEntry` too), for the same
|
|
176
|
+
* reason. A `Dirent` from `readdir(withFileTypes)` never follows one, so
|
|
177
|
+
* `isFile()` and `isDirectory()` are both false for a
|
|
178
|
+
* symlinked `.feature`: it was invisible to the orphan scan (`loam gherkin`
|
|
179
|
+
* left it behind forever) and invisible to the staleness scan (it graded as no
|
|
180
|
+
* coverage at all). Composing a suite out of links is legitimate, so they are
|
|
181
|
+
* followed rather than refused, and a dangling one is skipped exactly as an
|
|
182
|
+
* absent file is.
|
|
183
|
+
*/
|
|
184
|
+
export async function featureFilesUnder(root) {
|
|
185
|
+
const out = [];
|
|
186
|
+
// Following links means a cycle is now possible; every directory is entered
|
|
187
|
+
// once by its resolved path (`markDescended`), so a loop terminates instead
|
|
188
|
+
// of hanging.
|
|
189
|
+
const entered = new Set();
|
|
190
|
+
const walk = async (dir) => {
|
|
191
|
+
if (!markDescended(dir, entered))
|
|
192
|
+
return;
|
|
193
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
194
|
+
if (entry.name.startsWith("."))
|
|
195
|
+
continue;
|
|
196
|
+
const abs = join(dir, entry.name);
|
|
197
|
+
if (entryIs(dir, entry, "dir"))
|
|
198
|
+
await walk(abs);
|
|
199
|
+
else if (entry.name.endsWith(".feature") && entryIs(dir, entry, "file"))
|
|
200
|
+
out.push(abs);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
await walk(root);
|
|
204
|
+
return out.sort((a, b) => {
|
|
205
|
+
const ra = a.split(sep).join("/");
|
|
206
|
+
const rb = b.split(sep).join("/");
|
|
207
|
+
return ra < rb ? -1 : ra > rb ? 1 : 0;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=stale.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stale.js","sourceRoot":"","sources":["../../../src/core/gherkin/stale.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What `loam validate` reports about a generated suite: which scenarios moved\n * out from under it, and which were never generated at all.\n *\n * Service-repo-scoped, and silent until a suite exists — a service that never\n * ran `loam gherkin` has not opted into the axis and is not failing it. The\n * whole comparison is by digest rather than by name, so a reworded scenario\n * reads as stale rather than as missing plus new.\n */\nimport { existsSync } from \"node:fs\";\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { join, relative, sep } from \"node:path\";\nimport type { PathableService } from \"../kernel/ids/service.js\";\nimport { SPEC_AXES } from \"../repo/paths.js\";\nimport { locateServicePaths } from \"../repo/service-target.js\";\nimport { listFeatures } from \"../repo/repo.js\";\nimport { entryIs, markDescended } from \"../repo/tree/fs.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport type { Finding } from \"../vocabulary/report.js\";\nimport { UnsafePathError } from \"../kernel/path-safety.js\";\nimport { gherkinRoot, scenarioDigest } from \"./stamp.js\";\nimport { axisLabel } from \"./emit.js\";\nimport { parseStampedFeature, type StampedFeature } from \"./read.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\n\n/**\n * The gherkin freshness chain, service-repo-scoped like `sources.*`: it needs\n * the repo (the suite lives there), and it stays quiet until\n * `<gherkinDir>/loam/` exists — a service that never generated has not opted\n * in, and silence there is honest, not vacuous.\n *\n * The LIVING specs are the reference, with one carve-out. A stamped file\n * tagged with a feature still in flight answers to that feature's delta, not\n * to the living spec its requirements have not merged into yet — so it is\n * skipped here (its regeneration is `loam gherkin <FEAT>`), and the moment the\n * feature archives, its requirements ARE living requirements, digests\n * unchanged, and the same file grades current with no rewrite. A tag naming\n * no active feature (the feature was archived, or abandoned) gets no\n * exemption: the living spec is all there is to answer to.\n *\n * Three warnings, one per way the suite and the spec can disagree, judged\n * digest-first and by requirement name second:\n *\n * - `gherkin.missing` — a living scenario whose digest no stamped scenario of\n * its axis carries: the suite has no test for these words.\n * - `gherkin.stale` — a stamped scenario whose digest matches no living\n * scenario while its file's requirement still exists: the spec moved under\n * the generated file. (A reworded scenario therefore reports both: the old\n * words are stale, the new words are missing — one regeneration clears\n * both.)\n * - `gherkin.orphaned` — a whole file whose requirement no longer exists in\n * its axis's living spec; reported once per file, because every scenario in\n * it is moot together.\n *\n * Digests are content identity within one service and one axis, deliberately:\n * two identically-worded scenarios of one service's one axis share one digest,\n * so one stamped copy covers both — while the axes stay separate namespaces\n * twice over (every comparison here is axis-scoped, and the recipe salts the\n * arch axis), and the services stay separate because the recipe salts the\n * service too. That last salt is why a stamped file copied out of another\n * service's repository grades `gherkin.orphaned`/`gherkin.stale` here instead\n * of quietly counting as this service's coverage.\n *\n * All warnings — `--strict` is the CI escalation — plus one `ok` confirmation\n * (`gherkin.current`, the `sources.current` pattern) when a generated suite\n * exists and nothing disagrees.\n */\nexport async function gherkinFindings(ctx: {\n docsDir: DocsDir;\n service: PathableService;\n /** The service repo, when loam is standing in it. Undefined disables the chain, like sources.*. */\n repoDir?: string;\n gherkinDir?: string;\n fleet: FleetContext;\n}): Promise<Finding[]> {\n if (ctx.repoDir === undefined) return [];\n let root: string;\n try {\n root = gherkinRoot(ctx.repoDir, ctx.gherkinDir);\n } catch (err) {\n if (!(err instanceof UnsafePathError)) throw err;\n return [\n {\n severity: \"error\",\n code: \"gherkin.path-outside\",\n subject: ctx.service,\n message: `The configured Gherkin output is unsafe: ${err.message}. It must stay inside the service repo.`,\n },\n ];\n }\n if (!existsSync(root)) return [];\n\n // The living reference, per axis: requirement names, and scenario digests.\n interface AxisState {\n file: string;\n reqNames: Set<string>;\n digests: Set<string>;\n scenarios: Array<{ req: string; name: string; digest: string }>;\n }\n const paths = await locateServicePaths(ctx.docsDir, ctx.service, ctx.fleet);\n const axes = new Map<\"business\" | \"arch\", AxisState>();\n for (const axis of SPEC_AXES) {\n const label = axisLabel(axis);\n const state: AxisState = { file: axis.file, reqNames: new Set(), digests: new Set(), scenarios: [] };\n const path = paths[axis.key];\n if (existsSync(path)) {\n for (const r of await ctx.fleet.readRequirements(path)) {\n if (r.kind === \"REMOVED\") continue;\n state.reqNames.add(r.name);\n for (const s of r.scenarios) {\n const digest = scenarioDigest(ctx.service, s.lines, label);\n state.digests.add(digest);\n state.scenarios.push({ req: r.name, name: s.name, digest });\n }\n }\n }\n axes.set(label, state);\n }\n\n // The stamped side. Every stamped digest counts toward coverage — an\n // in-flight file whose scenario matches the living words IS a test for them.\n // `Set<string>`, not the ids' own brand: this set answers membership\n // questions for tags read out of .feature files — document text.\n const active = new Set<string>((await listFeatures(ctx.docsDir, {}, ctx.fleet)).map((f) => f.id));\n const stampedDigests = { business: new Set<string>(), arch: new Set<string>() };\n const parsed: Array<{ rel: string; axis: \"business\" | \"arch\"; file: StampedFeature }> = [];\n let stampedScenarios = 0;\n for (const abs of await featureFilesUnder(root)) {\n const file = parseStampedFeature(await readFile(abs, \"utf8\"));\n if (file === null) continue;\n const axis = file.tags.includes(\"architecture\") ? \"arch\" : \"business\";\n for (const s of file.scenarios) stampedDigests[axis].add(s.digest);\n stampedScenarios += file.scenarios.length;\n parsed.push({ rel: relative(ctx.repoDir, abs).split(sep).join(\"/\"), axis, file });\n }\n\n const findings: Finding[] = [];\n const regen = `\\`loam gherkin --service ${ctx.service}\\``;\n for (const [axis, state] of axes) {\n for (const sc of state.scenarios) {\n if (stampedDigests[axis].has(sc.digest)) continue;\n findings.push({\n severity: \"warn\",\n code: \"gherkin.missing\",\n subject: ctx.service,\n message: `${ctx.service}: scenario '${sc.name}' of requirement '${sc.req}' (${state.file}) has no generated .feature scenario — the suite has no test for these words; ${regen} regenerates it`,\n });\n }\n }\n for (const { rel, axis, file } of parsed) {\n if (file.tags.some((t) => active.has(t))) continue;\n const state = axes.get(axis)!;\n if (file.featureName === null || !state.reqNames.has(file.featureName)) {\n findings.push({\n severity: \"warn\",\n code: \"gherkin.orphaned\",\n subject: ctx.service,\n message: `${ctx.service}: ${rel}: requirement '${file.featureName ?? \"(unnamed)\"}' no longer exists in the living ${state.file} — the whole file is orphaned; ${regen} removes it`,\n });\n continue;\n }\n for (const s of file.scenarios) {\n if (state.digests.has(s.digest)) continue;\n findings.push({\n severity: \"warn\",\n code: \"gherkin.stale\",\n subject: ctx.service,\n message: `${ctx.service}: ${rel}: scenario '${s.name}' matches no scenario in the living ${state.file} — the spec moved under the generated file; ${regen} regenerates it`,\n });\n }\n }\n if (findings.length === 0) {\n findings.push({\n severity: \"ok\",\n code: \"gherkin.current\",\n message: `${ctx.service}: generated gherkin current (${parsed.length} file(s), ${stampedScenarios} stamped scenario(s) agree with the living specs)`,\n });\n }\n return findings;\n}\n\n/**\n * Every `.feature` file at or under `root`, sorted by repo-relative path —\n * enumeration is part of the output contract, so the order cannot depend on\n * the filesystem's. Dot-entries are skipped, like every other walk in loam.\n *\n * Symlinks are FOLLOWED, through `repo/tree/fs.ts`'s `entryIs` — the rule\n * every enumeration shares (brief/brief.ts's `hasEntry` too), for the same\n * reason. A `Dirent` from `readdir(withFileTypes)` never follows one, so\n * `isFile()` and `isDirectory()` are both false for a\n * symlinked `.feature`: it was invisible to the orphan scan (`loam gherkin`\n * left it behind forever) and invisible to the staleness scan (it graded as no\n * coverage at all). Composing a suite out of links is legitimate, so they are\n * followed rather than refused, and a dangling one is skipped exactly as an\n * absent file is.\n */\nexport async function featureFilesUnder(root: string): Promise<string[]> {\n const out: string[] = [];\n // Following links means a cycle is now possible; every directory is entered\n // once by its resolved path (`markDescended`), so a loop terminates instead\n // of hanging.\n const entered = new Set<string>();\n const walk = async (dir: string): Promise<void> => {\n if (!markDescended(dir, entered)) return;\n for (const entry of await readdir(dir, { withFileTypes: true })) {\n if (entry.name.startsWith(\".\")) continue;\n const abs = join(dir, entry.name);\n if (entryIs(dir, entry, \"dir\")) await walk(abs);\n else if (entry.name.endsWith(\".feature\") && entryIs(dir, entry, \"file\")) out.push(abs);\n }\n };\n await walk(root);\n return out.sort((a, b) => {\n const ra = a.split(sep).join(\"/\");\n const rb = b.split(sep).join(\"/\");\n return ra < rb ? -1 : ra > rb ? 1 : 0;\n });\n}\n"]}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Gherkin emission: spec scenarios become real `.feature` files.
|
|
3
|
+
*
|
|
4
|
+
* Scenarios have been "the source for tests" since SCHEMA.md's first draft, but
|
|
5
|
+
* until now they only left the docs repo as projected markdown — prose a coding
|
|
6
|
+
* agent could paraphrase into something easier to pass. The emission closes the
|
|
7
|
+
* gap mechanically: one `.feature` file per requirement, steps taken from the
|
|
8
|
+
* scenario's own bullet lines, and a digest TAG (`@loam-digest-<16hex>`) on
|
|
9
|
+
* every scenario using the SAME body-hash recipe `loam verify` folds into its
|
|
10
|
+
* claim ids (`./digest.ts`'s scenarioBodyHash — salted by the owning service and
|
|
11
|
+
* by the spec axis, so a stamp belongs to one suite and one axis) — so the
|
|
12
|
+
* test skeleton, the claim and the spec can never quietly disagree about what
|
|
13
|
+
* a scenario says, and no two repositories can answer each other's. A tag
|
|
14
|
+
* rather than a comment because tags are what cucumber's JSON report carries
|
|
15
|
+
* per scenario: the stamp rides through the runner untouched, which is what
|
|
16
|
+
* lets `loam verify --results` match a report scenario back to its claim.
|
|
17
|
+
*
|
|
18
|
+
* The files land in the SERVICE repo — the docs repo states the questions, the
|
|
19
|
+
* tests live with the code — under `<gherkinDir>/loam/`. That `loam/`
|
|
20
|
+
* subdirectory is loam's own derived space: regeneration rewrites its scope and
|
|
21
|
+
* removes its orphans, and loam never writes or deletes a byte outside it.
|
|
22
|
+
* Everything here is deterministic on purpose: the same specs yield the same
|
|
23
|
+
* bytes, which is what lets `loam validate` grade staleness by digest instead
|
|
24
|
+
* of by guesswork.
|
|
25
|
+
*/
|
|
26
|
+
import { join } from "node:path";
|
|
27
|
+
import { scenarioBodyHash } from "./digest.js";
|
|
28
|
+
import { resolveInside } from "../kernel/path-safety.js";
|
|
29
|
+
/** Where Gherkin lives in a service repo when loam.json does not say. */
|
|
30
|
+
const DEFAULT_GHERKIN_DIR = "features";
|
|
31
|
+
/** The subdirectory loam owns outright. Everything loam writes stays inside it. */
|
|
32
|
+
const GHERKIN_SUBDIR = "loam";
|
|
33
|
+
/** How much of the scenario body hash goes into a `@loam-digest-…` tag. */
|
|
34
|
+
export const GHERKIN_DIGEST_LENGTH = 16;
|
|
35
|
+
/** The one directory `loam gherkin` may touch: `<repo>/<gherkinDir>/loam/`. */
|
|
36
|
+
export function gherkinRoot(repoDir, gherkinDir) {
|
|
37
|
+
return resolveInside(repoDir, join(gherkinDir ?? DEFAULT_GHERKIN_DIR, GHERKIN_SUBDIR), "gherkin output directory");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The digest stamped onto every generated scenario: verify's scenario body
|
|
41
|
+
* hash, at the sources-digest length. Content identity WITHIN one service and
|
|
42
|
+
* one axis, not provenance — two identically-worded scenarios of one service's
|
|
43
|
+
* one axis share a digest, and that is the point: the stamp answers "does this
|
|
44
|
+
* suite still contain a test for THESE words".
|
|
45
|
+
*
|
|
46
|
+
* Both salts ride in the hash (see `scenarioBodyHash`), so the namespaces stay
|
|
47
|
+
* separate all the way into a cucumber report: a business run can never answer
|
|
48
|
+
* for an arch scenario, and one service's green run can never answer another
|
|
49
|
+
* service's claim just because the two specs spell the sentence the same way.
|
|
50
|
+
* `service` is required and never defaulted — a stamp that forgot it would be
|
|
51
|
+
* a tag no claim matches, and the fleet bug it exists to close would come back
|
|
52
|
+
* silently.
|
|
53
|
+
*/
|
|
54
|
+
export function scenarioDigest(service, lines, axis = "business") {
|
|
55
|
+
return scenarioBodyHash(service, lines, axis).slice(0, GHERKIN_DIGEST_LENGTH);
|
|
56
|
+
}
|
|
57
|
+
/** The digest as the tag on the line above each `Scenario:`. */
|
|
58
|
+
export function scenarioDigestTag(digest) {
|
|
59
|
+
return `@loam-digest-${digest}`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A tag token carrying a scenario digest — in a generated file, or in a
|
|
63
|
+
* cucumber JSON report. The leading `@` is optional because some runners
|
|
64
|
+
* (behave) strip it from the tags they report.
|
|
65
|
+
*/
|
|
66
|
+
export const DIGEST_TAG_RE = /^@?loam-digest-([0-9a-f]{16})$/;
|
|
67
|
+
/**
|
|
68
|
+
* Line 1 of every generated file — the AGENTS.md stamp pattern, in Gherkin
|
|
69
|
+
* comment syntax. The version records which loam wrote the file; the rest is
|
|
70
|
+
* the ownership rule in the reader's face, because `loam/` files are rewritten
|
|
71
|
+
* wholesale on regeneration and a hand edit would be silently destroyed.
|
|
72
|
+
*/
|
|
73
|
+
export function gherkinStampLine(version) {
|
|
74
|
+
return `# generated by loam v${version} — \`loam gherkin\`; do not hand-edit, regenerate instead`;
|
|
75
|
+
}
|
|
76
|
+
export const STAMP_RE = /^#\s*generated by loam v(\S+)/;
|
|
77
|
+
//# sourceMappingURL=stamp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stamp.js","sourceRoot":"","sources":["../../../src/core/gherkin/stamp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAqB,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,yEAAyE;AACzE,MAAM,mBAAmB,GAAG,UAAU,CAAC;AAEvC,mFAAmF;AACnF,MAAM,cAAc,GAAG,MAAM,CAAC;AAE9B,2EAA2E;AAC3E,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAExC,+EAA+E;AAC/E,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,UAAmB;IAC9D,OAAO,aAAa,CAClB,OAAO,EACP,IAAI,CAAC,UAAU,IAAI,mBAAmB,EAAE,cAAc,CAAC,EACvD,0BAA0B,CAC3B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,KAAe,EACf,IAAI,GAAiB,UAAU;IAE/B,OAAO,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,CAAC,CAAC;AAChF,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,OAAO,gBAAgB,MAAM,EAAE,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,gCAAgC,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,OAAO,wBAAwB,OAAO,2DAA2D,CAAC;AACpG,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,+BAA+B,CAAC","sourcesContent":["/**\n * The Gherkin emission: spec scenarios become real `.feature` files.\n *\n * Scenarios have been \"the source for tests\" since SCHEMA.md's first draft, but\n * until now they only left the docs repo as projected markdown — prose a coding\n * agent could paraphrase into something easier to pass. The emission closes the\n * gap mechanically: one `.feature` file per requirement, steps taken from the\n * scenario's own bullet lines, and a digest TAG (`@loam-digest-<16hex>`) on\n * every scenario using the SAME body-hash recipe `loam verify` folds into its\n * claim ids (`./digest.ts`'s scenarioBodyHash — salted by the owning service and\n * by the spec axis, so a stamp belongs to one suite and one axis) — so the\n * test skeleton, the claim and the spec can never quietly disagree about what\n * a scenario says, and no two repositories can answer each other's. A tag\n * rather than a comment because tags are what cucumber's JSON report carries\n * per scenario: the stamp rides through the runner untouched, which is what\n * lets `loam verify --results` match a report scenario back to its claim.\n *\n * The files land in the SERVICE repo — the docs repo states the questions, the\n * tests live with the code — under `<gherkinDir>/loam/`. That `loam/`\n * subdirectory is loam's own derived space: regeneration rewrites its scope and\n * removes its orphans, and loam never writes or deletes a byte outside it.\n * Everything here is deterministic on purpose: the same specs yield the same\n * bytes, which is what lets `loam validate` grade staleness by digest instead\n * of by guesswork.\n */\nimport { join } from \"node:path\";\nimport { scenarioBodyHash, type ScenarioAxis } from \"./digest.js\";\nimport { resolveInside } from \"../kernel/path-safety.js\";\n\n/** Where Gherkin lives in a service repo when loam.json does not say. */\nconst DEFAULT_GHERKIN_DIR = \"features\";\n\n/** The subdirectory loam owns outright. Everything loam writes stays inside it. */\nconst GHERKIN_SUBDIR = \"loam\";\n\n/** How much of the scenario body hash goes into a `@loam-digest-…` tag. */\nexport const GHERKIN_DIGEST_LENGTH = 16;\n\n/** The one directory `loam gherkin` may touch: `<repo>/<gherkinDir>/loam/`. */\nexport function gherkinRoot(repoDir: string, gherkinDir?: string): string {\n return resolveInside(\n repoDir,\n join(gherkinDir ?? DEFAULT_GHERKIN_DIR, GHERKIN_SUBDIR),\n \"gherkin output directory\",\n );\n}\n\n/**\n * The digest stamped onto every generated scenario: verify's scenario body\n * hash, at the sources-digest length. Content identity WITHIN one service and\n * one axis, not provenance — two identically-worded scenarios of one service's\n * one axis share a digest, and that is the point: the stamp answers \"does this\n * suite still contain a test for THESE words\".\n *\n * Both salts ride in the hash (see `scenarioBodyHash`), so the namespaces stay\n * separate all the way into a cucumber report: a business run can never answer\n * for an arch scenario, and one service's green run can never answer another\n * service's claim just because the two specs spell the sentence the same way.\n * `service` is required and never defaulted — a stamp that forgot it would be\n * a tag no claim matches, and the fleet bug it exists to close would come back\n * silently.\n */\nexport function scenarioDigest(\n service: string,\n lines: string[],\n axis: ScenarioAxis = \"business\",\n): string {\n return scenarioBodyHash(service, lines, axis).slice(0, GHERKIN_DIGEST_LENGTH);\n}\n\n/** The digest as the tag on the line above each `Scenario:`. */\nexport function scenarioDigestTag(digest: string): string {\n return `@loam-digest-${digest}`;\n}\n\n/**\n * A tag token carrying a scenario digest — in a generated file, or in a\n * cucumber JSON report. The leading `@` is optional because some runners\n * (behave) strip it from the tags they report.\n */\nexport const DIGEST_TAG_RE = /^@?loam-digest-([0-9a-f]{16})$/;\n\n/**\n * Line 1 of every generated file — the AGENTS.md stamp pattern, in Gherkin\n * comment syntax. The version records which loam wrote the file; the rest is\n * the ownership rule in the reader's face, because `loam/` files are rewritten\n * wholesale on regeneration and a hand edit would be silently destroyed.\n */\nexport function gherkinStampLine(version: string): string {\n return `# generated by loam v${version} — \\`loam gherkin\\`; do not hand-edit, regenerate instead`;\n}\n\nexport const STAMP_RE = /^#\\s*generated by loam v(\\S+)/;\n\n"]}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The step catalogue: which phrases somebody has DECIDED the suite defines.
|
|
3
|
+
*
|
|
4
|
+
* ## The decision this file records, because it was the open question
|
|
5
|
+
*
|
|
6
|
+
* `./inventory.ts` answers how many definitions a suite needs. It stops there on
|
|
7
|
+
* purpose — no file, no finding, no stable code — and the gap it leaves is the
|
|
8
|
+
* next ARTIFACT, not the next warning: a team reads the report, writes the glue
|
|
9
|
+
* by hand, and nothing anywhere records which phrases now have definitions, so
|
|
10
|
+
* the report is recomputed from scratch every time instead of diffed against a
|
|
11
|
+
* decision somebody made.
|
|
12
|
+
*
|
|
13
|
+
* The catalogue is **AUTHORED**, not emitted, and it lives in the DOCS repo at
|
|
14
|
+
* `services/<svc>/steps.yaml`. Four things decided it:
|
|
15
|
+
*
|
|
16
|
+
* - It is a DECISION, not an observation. loam does not read code and must
|
|
17
|
+
* never present a derived thing as truth (the non-goal is written down); what
|
|
18
|
+
* a team can say is "these are the phrases we have agreed to define", and
|
|
19
|
+
* that sentence is an authored document by construction. An emitted
|
|
20
|
+
* catalogue would have to be derived from a step registry loam cannot read.
|
|
21
|
+
* - `loam steps` NEEDS NO SERVICE REPO to stand in — that is its own stated
|
|
22
|
+
* design, and it reads the living specs rather than the emitted `.feature`
|
|
23
|
+
* files precisely so it answers the same way in a repo that has never run
|
|
24
|
+
* `loam gherkin`. A catalogue in the service repo would make the comparison
|
|
25
|
+
* require one, which is a different command.
|
|
26
|
+
* - The shape rule the glossary and capability trees already settled: an entry
|
|
27
|
+
* with prose gets a file, an entry WITHOUT prose stays a line in YAML. A
|
|
28
|
+
* catalogue entry is a phrase and nothing else, so it is a line in YAML — the
|
|
29
|
+
* same side of that rule `architecture/capabilities.yaml` and
|
|
30
|
+
* `permissions.yaml` sit on.
|
|
31
|
+
* - Nothing generated may land inside `<gherkinDir>/loam/`, which `loam
|
|
32
|
+
* gherkin` owns and overwrites wholesale. Living in the docs repo satisfies
|
|
33
|
+
* that by construction rather than by a rule somebody has to remember.
|
|
34
|
+
*
|
|
35
|
+
* SERVICE-SCOPED, beside `health.yaml`, because a step registry belongs to one
|
|
36
|
+
* suite and a suite belongs to one service. A fleet-level catalogue would claim
|
|
37
|
+
* one registry across services that do not share a test runner.
|
|
38
|
+
*
|
|
39
|
+
* ## What it does NOT do, and this is the other half of the decision
|
|
40
|
+
*
|
|
41
|
+
* **It gains no stable code, and `loam validate` never reads it.** A phrase
|
|
42
|
+
* written but not catalogued is a work-list row, not a breach: writing a spec
|
|
43
|
+
* ahead of the glue is the normal order, and a check that fires on it is the
|
|
44
|
+
* kind a fleet turns off — at which point the real findings beside it are turned
|
|
45
|
+
* off too. The near-duplicate groups stay a report for the same reason. So this
|
|
46
|
+
* axis costs one document kind and zero codes, and everything it produces is
|
|
47
|
+
* columns in `loam steps`.
|
|
48
|
+
*
|
|
49
|
+
* **It claims nothing about whether the glue exists.** A catalogued phrase is a
|
|
50
|
+
* phrase somebody said they would define; loam cannot see a step registry and
|
|
51
|
+
* does not pretend to. The two columns are named for what they actually mean —
|
|
52
|
+
* written-not-catalogued and catalogued-not-written — and neither says "missing
|
|
53
|
+
* definition".
|
|
54
|
+
*
|
|
55
|
+
* ## The grammar
|
|
56
|
+
*
|
|
57
|
+
* ```yaml
|
|
58
|
+
* steps:
|
|
59
|
+
* - a payment of 100.00
|
|
60
|
+
* - Given it is split 60/40
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* A list of step TEXTS, not of keys. The key is loam's own spelling — `{n}`,
|
|
64
|
+
* `{s}`, `{p}` collapse, keyword stripped (`./phrase.ts`) — and asking an author
|
|
65
|
+
* to write it would make an internal normalisation a hand-typed contract, where
|
|
66
|
+
* one wrong brace silently catalogues nothing. So an entry is written the way a
|
|
67
|
+
* step is written, with or without its keyword, and loam keys it the same way it
|
|
68
|
+
* keys the written steps it is being compared against. Two entries that collapse
|
|
69
|
+
* onto one key are reported as such rather than merged: that is the author
|
|
70
|
+
* cataloguing one definition twice, which is exactly what the near-duplicate
|
|
71
|
+
* report exists to surface.
|
|
72
|
+
*/
|
|
73
|
+
import { existsSync } from "node:fs";
|
|
74
|
+
import { readFile } from "node:fs/promises";
|
|
75
|
+
import { parse as parseYaml } from "yaml";
|
|
76
|
+
import { isRecord } from "../../kernel/records.js";
|
|
77
|
+
import { compareIds } from "../../repo/entries.js";
|
|
78
|
+
import { phraseOf } from "./phrase.js";
|
|
79
|
+
import {} from "./inventory.js";
|
|
80
|
+
const ABSENT = { present: false, entries: [] };
|
|
81
|
+
/**
|
|
82
|
+
* Read one service's catalogue.
|
|
83
|
+
*
|
|
84
|
+
* A missing file is silence, not an error: the axis's opt-in is the file's
|
|
85
|
+
* existence, exactly as `capabilities/`'s is a directory's. Anything present and
|
|
86
|
+
* unparseable is `unreadable` with the reason — never an empty catalogue, which
|
|
87
|
+
* would report every written phrase as uncatalogued and read as a work-list
|
|
88
|
+
* somebody has to do.
|
|
89
|
+
*/
|
|
90
|
+
export async function readStepCatalogue(path) {
|
|
91
|
+
if (!existsSync(path))
|
|
92
|
+
return ABSENT;
|
|
93
|
+
let doc;
|
|
94
|
+
try {
|
|
95
|
+
doc = parseYaml(await readFile(path, "utf8"));
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
return { present: true, entries: [], unreadable: e instanceof Error ? e.message : String(e) };
|
|
99
|
+
}
|
|
100
|
+
if (doc === null || doc === undefined)
|
|
101
|
+
return { present: true, entries: [] };
|
|
102
|
+
if (!isRecord(doc))
|
|
103
|
+
return { present: true, entries: [], unreadable: "the document is not a mapping" };
|
|
104
|
+
const block = doc["steps"];
|
|
105
|
+
if (block === undefined)
|
|
106
|
+
return { present: true, entries: [] };
|
|
107
|
+
if (!Array.isArray(block)) {
|
|
108
|
+
return { present: true, entries: [], unreadable: "`steps` is not a list of step texts" };
|
|
109
|
+
}
|
|
110
|
+
const entries = [];
|
|
111
|
+
for (const item of block) {
|
|
112
|
+
// A non-string entry refuses the whole file rather than being skipped. A
|
|
113
|
+
// skipped entry is a phrase silently absent from the catalogue, which prints
|
|
114
|
+
// as "written but not catalogued" — a work-list row invented by a shape
|
|
115
|
+
// error, which is the wrong answer this fails closed to avoid.
|
|
116
|
+
if (typeof item !== "string") {
|
|
117
|
+
return { present: true, entries: [], unreadable: "every `steps` entry must be a step text (a string)" };
|
|
118
|
+
}
|
|
119
|
+
entries.push({ text: item, key: phraseOf(item).key });
|
|
120
|
+
}
|
|
121
|
+
return { present: true, entries };
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Written phrases against catalogued ones.
|
|
125
|
+
*
|
|
126
|
+
* ORDER IS THE INVENTORY'S for `uncatalogued` — `inv.phrases` is already sorted
|
|
127
|
+
* count-descending then key-ascending, so the work-list comes out with the
|
|
128
|
+
* phrase that pays back most first, and stays diffable. `unwritten` and
|
|
129
|
+
* `duplicated` have no count to sort on and are sorted by key.
|
|
130
|
+
*
|
|
131
|
+
* The catalogue's own `present`/`unreadable` state is NOT consulted here: this
|
|
132
|
+
* is the comparison, and whether it was worth making is the caller's question.
|
|
133
|
+
* Handing it an unreadable catalogue would produce a full `uncatalogued` list
|
|
134
|
+
* that reads exactly like a real work-list, which is why the caller checks first.
|
|
135
|
+
*/
|
|
136
|
+
export function compareToCatalogue(inv, catalogue) {
|
|
137
|
+
const catalogued = new Set(catalogue.entries.map((e) => e.key));
|
|
138
|
+
const written = new Set(inv.phrases.map((p) => p.key));
|
|
139
|
+
const bySpelling = new Map();
|
|
140
|
+
for (const entry of catalogue.entries) {
|
|
141
|
+
const texts = bySpelling.get(entry.key) ?? [];
|
|
142
|
+
texts.push(entry.text);
|
|
143
|
+
bySpelling.set(entry.key, texts);
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
uncatalogued: inv.phrases.filter((p) => !catalogued.has(p.key)).map((p) => p.key),
|
|
147
|
+
unwritten: [...catalogued].filter((key) => !written.has(key)).sort(compareIds),
|
|
148
|
+
duplicated: [...bySpelling]
|
|
149
|
+
.filter(([, texts]) => texts.length > 1)
|
|
150
|
+
.map(([key, texts]) => ({ key, texts }))
|
|
151
|
+
.sort((a, b) => compareIds(a.key, b.key)),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=catalogue.js.map
|