@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,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fleet.yaml — the tiny human-authored file `loam seed` templates the fleet
|
|
3
|
+
* map from. A human states the facts (which services exist, who calls whom);
|
|
4
|
+
* loam does mechanical templating and guesses nothing, which is what keeps
|
|
5
|
+
* seed on the same side of the no-extractor line as `loam new`.
|
|
6
|
+
*
|
|
7
|
+
* Parsed with the AST (`parseDocument` + `LineCounter`) rather than `parse`,
|
|
8
|
+
* deliberately: every refusal names `file:line`, because the file is edited by
|
|
9
|
+
* hand and "something in it is wrong" is not a fix. Fail-closed throughout —
|
|
10
|
+
* an unknown key, a non-string entry, a malformed call all refuse before the
|
|
11
|
+
* caller writes a single byte; a partially-understood fleet file must never
|
|
12
|
+
* become a partially-right landscape. The node-level readers live in
|
|
13
|
+
* `./items.ts`; this module owns what their answers must amount to.
|
|
14
|
+
*
|
|
15
|
+
* The grammars are the shared ones and never a private copy: service ids
|
|
16
|
+
* through `parseServiceId`, subsystem names through `parseSubsystemName`,
|
|
17
|
+
* externals through the same `dirNameHazard` those two are built on (an
|
|
18
|
+
* external never becomes a directory, but calls name services and externals
|
|
19
|
+
* out of ONE namespace, and a name legal as one and illegal as the other
|
|
20
|
+
* would make the duplicate refusal incoherent — `kernel/ids/service.ts`'s own
|
|
21
|
+
* argument, one level up).
|
|
22
|
+
*/
|
|
23
|
+
import { isMap, isScalar, LineCounter, parseDocument } from "yaml";
|
|
24
|
+
import { dirNameHazard, parseServiceId } from "../../kernel/ids/service.js";
|
|
25
|
+
import { parseSubsystemName } from "../../kernel/ids/subsystem.js";
|
|
26
|
+
import { at, hint, listOf, serviceEntry, stringOf } from "./items.js";
|
|
27
|
+
const TOP_KEYS = ["services", "subsystems", "externals", "calls"];
|
|
28
|
+
function invalid(message) {
|
|
29
|
+
return { ok: false, problem: { kind: "invalid", message } };
|
|
30
|
+
}
|
|
31
|
+
/** Parse and validate the fleet file's text. Never throws for an expected outcome. */
|
|
32
|
+
export function readFleetFile(text, file) {
|
|
33
|
+
const lines = new LineCounter();
|
|
34
|
+
const doc = parseDocument(text, { lineCounter: lines });
|
|
35
|
+
const ctx = { file, lines };
|
|
36
|
+
const parseError = doc.errors[0];
|
|
37
|
+
if (parseError !== undefined) {
|
|
38
|
+
const line = parseError.linePos?.[0]?.line;
|
|
39
|
+
return invalid(`${file}${line === undefined ? "" : `:${line}`} cannot be read as YAML — ` +
|
|
40
|
+
`${parseError.message.split("\n")[0]}`);
|
|
41
|
+
}
|
|
42
|
+
if (!isMap(doc.contents)) {
|
|
43
|
+
return invalid(`${file} must be a YAML mapping with a \`services:\` list (and optional ` +
|
|
44
|
+
`\`subsystems:\`, \`externals:\`, \`calls:\`) — the whole document is ` +
|
|
45
|
+
`${doc.contents === null ? "empty" : "not a mapping"}.`);
|
|
46
|
+
}
|
|
47
|
+
// The top level, fail-closed: a mistyped key is a whole section silently
|
|
48
|
+
// dropped, which is exactly the partial scaffold this reader must refuse.
|
|
49
|
+
const sections = new Map();
|
|
50
|
+
for (const pair of doc.contents.items) {
|
|
51
|
+
const key = isScalar(pair.key) && typeof pair.key.value === "string" ? pair.key.value : null;
|
|
52
|
+
if (key === null || !TOP_KEYS.includes(key)) {
|
|
53
|
+
const spelled = key ?? String(pair.key);
|
|
54
|
+
return invalid(`${at(ctx, pair.key)} — unknown key '${spelled}': the fleet file's keys are ` +
|
|
55
|
+
`services, subsystems, externals and calls.${hint(spelled, TOP_KEYS)}`);
|
|
56
|
+
}
|
|
57
|
+
sections.set(key, pair.value);
|
|
58
|
+
}
|
|
59
|
+
// One flat namespace over everything a name can be declared as, because a
|
|
60
|
+
// call endpoint must name exactly one thing — the same rule the tree walk
|
|
61
|
+
// enforces over `services/` (`subsystem.name-collision`).
|
|
62
|
+
const declared = new Map();
|
|
63
|
+
const claim = (name, where, role) => {
|
|
64
|
+
const held = declared.get(name);
|
|
65
|
+
if (held !== undefined) {
|
|
66
|
+
return {
|
|
67
|
+
kind: "duplicate",
|
|
68
|
+
message: `${where} declares '${name}' as ${role}, but ${held.where} already declares it as ` +
|
|
69
|
+
`${held.role} — service ids, subsystem names and externals share one flat namespace ` +
|
|
70
|
+
`(a call endpoint must name exactly one thing). Rename or remove one, then re-run.`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
declared.set(name, { where, role });
|
|
74
|
+
return null;
|
|
75
|
+
};
|
|
76
|
+
const subsystems = [];
|
|
77
|
+
for (const item of listOf(sections.get("subsystems"))) {
|
|
78
|
+
const raw = stringOf(item);
|
|
79
|
+
if (raw === null)
|
|
80
|
+
return invalid(`${at(ctx, item)} — a subsystems entry must be a string (quote it if YAML reads it as something else).`);
|
|
81
|
+
const parsed = parseSubsystemName(raw, "subsystems entry");
|
|
82
|
+
if (!parsed.ok)
|
|
83
|
+
return invalid(`${at(ctx, item)} — ${parsed.problem}`);
|
|
84
|
+
const dup = claim(raw, at(ctx, item), "a subsystem");
|
|
85
|
+
if (dup !== null)
|
|
86
|
+
return { ok: false, problem: dup };
|
|
87
|
+
subsystems.push(parsed.name);
|
|
88
|
+
}
|
|
89
|
+
const services = [];
|
|
90
|
+
/** Deferred so `subsystem: X` can reference a subsystem declared lower in the file. */
|
|
91
|
+
const subsystemRefs = [];
|
|
92
|
+
for (const item of listOf(sections.get("services"))) {
|
|
93
|
+
const entry = serviceEntry(ctx, item);
|
|
94
|
+
if ("invalid" in entry)
|
|
95
|
+
return invalid(entry.invalid);
|
|
96
|
+
const parsed = parseServiceId(entry.id, "services entry");
|
|
97
|
+
if (!parsed.ok)
|
|
98
|
+
return invalid(`${entry.where} — ${parsed.problem}`);
|
|
99
|
+
const dup = claim(entry.id, entry.where, "a service");
|
|
100
|
+
if (dup !== null)
|
|
101
|
+
return { ok: false, problem: dup };
|
|
102
|
+
if (entry.subsystem !== null)
|
|
103
|
+
subsystemRefs.push({ id: parsed.id, sub: entry.subsystem, where: entry.where });
|
|
104
|
+
services.push({ id: parsed.id, subsystem: null });
|
|
105
|
+
}
|
|
106
|
+
if (services.length === 0) {
|
|
107
|
+
return invalid(`${file} declares no services — \`services:\` is the one required key: list every ` +
|
|
108
|
+
`service id the fleet has (a bare string, or \`{ id, subsystem }\` to file one under a group).`);
|
|
109
|
+
}
|
|
110
|
+
const externals = [];
|
|
111
|
+
for (const item of listOf(sections.get("externals"))) {
|
|
112
|
+
const raw = stringOf(item);
|
|
113
|
+
if (raw === null)
|
|
114
|
+
return invalid(`${at(ctx, item)} — an externals entry must be a string (quote it if YAML reads it as something else).`);
|
|
115
|
+
if (dirNameHazard(raw) !== null) {
|
|
116
|
+
return invalid(`${at(ctx, item)} — '${raw}' cannot be an external's name: externals share the service-id ` +
|
|
117
|
+
`grammar (start with a letter or digit; letters, digits, '.', '_', '-'; no Windows-reserved ` +
|
|
118
|
+
`names), because calls name services and externals out of one namespace.`);
|
|
119
|
+
}
|
|
120
|
+
const dup = claim(raw, at(ctx, item), "an external");
|
|
121
|
+
if (dup !== null)
|
|
122
|
+
return { ok: false, problem: dup };
|
|
123
|
+
externals.push(raw);
|
|
124
|
+
}
|
|
125
|
+
// Subsystem references, now that every declaration is in: a `subsystem:`
|
|
126
|
+
// naming nothing is the second-likeliest typo in the file, and the hint
|
|
127
|
+
// offers only names the file really declares.
|
|
128
|
+
const subNames = new Set(subsystems);
|
|
129
|
+
for (const ref of subsystemRefs) {
|
|
130
|
+
if (!subNames.has(ref.sub)) {
|
|
131
|
+
return {
|
|
132
|
+
ok: false,
|
|
133
|
+
problem: {
|
|
134
|
+
kind: "unknown-subsystem",
|
|
135
|
+
message: `${ref.where} files service '${ref.id}' under '${ref.sub}', but \`subsystems:\` ` +
|
|
136
|
+
`declares no '${ref.sub}'.${hint(ref.sub, subsystems)} Add it to subsystems:, ` +
|
|
137
|
+
`or fix the spelling, then re-run.`,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// The branded name comes from the DECLARATION list, never from a second
|
|
143
|
+
// parse of the reference: `subsystems:` is where the grammar ran, and the
|
|
144
|
+
// membership check above proved every reference resolves into it.
|
|
145
|
+
const bySubName = new Map(subsystems.map((s) => [s, s]));
|
|
146
|
+
const filed = new Map(subsystemRefs.map((r) => [r.id, bySubName.get(r.sub) ?? null]));
|
|
147
|
+
const seededServices = services.map((s) => ({
|
|
148
|
+
id: s.id,
|
|
149
|
+
subsystem: filed.get(s.id) ?? null,
|
|
150
|
+
}));
|
|
151
|
+
const endpointProblem = (raw, name, where) => {
|
|
152
|
+
if (subNames.has(name)) {
|
|
153
|
+
return {
|
|
154
|
+
kind: "unknown-endpoint",
|
|
155
|
+
message: `${where} — call '${raw}' names the subsystem '${name}': a subsystem is a place ` +
|
|
156
|
+
`services live, never a call endpoint. Name one of its services instead.`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
kind: "unknown-endpoint",
|
|
161
|
+
message: `${where} — call '${raw}' names '${name}', which no services: or externals: entry ` +
|
|
162
|
+
`declares.${hint(name, [...services.map((s) => s.id), ...externals])} Declare it, ` +
|
|
163
|
+
`or fix the spelling, then re-run.`,
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
const callable = new Set([...services.map((s) => s.id), ...externals]);
|
|
167
|
+
const calls = [];
|
|
168
|
+
const seenCalls = new Set();
|
|
169
|
+
for (const item of listOf(sections.get("calls"))) {
|
|
170
|
+
const raw = stringOf(item);
|
|
171
|
+
if (raw === null)
|
|
172
|
+
return invalid(`${at(ctx, item)} — a calls entry must be a string of the form 'caller -> callee'.`);
|
|
173
|
+
const m = /^(\S+)\s*->\s*(\S+)$/.exec(raw.trim());
|
|
174
|
+
if (m === null) {
|
|
175
|
+
return invalid(`${at(ctx, item)} — a call is written 'caller -> callee' (got '${raw}').`);
|
|
176
|
+
}
|
|
177
|
+
const [from, to] = [m[1], m[2]];
|
|
178
|
+
for (const end of [from, to]) {
|
|
179
|
+
if (!callable.has(end))
|
|
180
|
+
return { ok: false, problem: endpointProblem(raw, end, at(ctx, item)) };
|
|
181
|
+
}
|
|
182
|
+
// LikeC4 has no self-edge: it reads `a -> a` as a parent-child
|
|
183
|
+
// relationship and refuses the whole file. Caught HERE, with the line,
|
|
184
|
+
// because the alternative is the self-check refusing `internal` over a
|
|
185
|
+
// fleet file whose fault it is not — and a copy-paste inside `calls:` is
|
|
186
|
+
// exactly how this line gets written.
|
|
187
|
+
if (from === to) {
|
|
188
|
+
return invalid(`${at(ctx, item)} — call '${raw}' names '${from}' at both ends, and LikeC4 has no ` +
|
|
189
|
+
`self-edge (it reads 'a -> a' as a parent-child relationship). Name the other ` +
|
|
190
|
+
`endpoint, or drop the line.`);
|
|
191
|
+
}
|
|
192
|
+
const key = `${from} ${to}`;
|
|
193
|
+
// A repeated line is the same fact stated twice, not a second edge.
|
|
194
|
+
if (seenCalls.has(key))
|
|
195
|
+
continue;
|
|
196
|
+
seenCalls.add(key);
|
|
197
|
+
calls.push({ from, to });
|
|
198
|
+
}
|
|
199
|
+
return { ok: true, seed: { services: seededServices, subsystems, externals, calls } };
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=fleet-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fleet-file.js","sourceRoot":"","sources":["../../../../src/core/c4/seed/fleet-file.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * fleet.yaml — the tiny human-authored file `loam seed` templates the fleet\n * map from. A human states the facts (which services exist, who calls whom);\n * loam does mechanical templating and guesses nothing, which is what keeps\n * seed on the same side of the no-extractor line as `loam new`.\n *\n * Parsed with the AST (`parseDocument` + `LineCounter`) rather than `parse`,\n * deliberately: every refusal names `file:line`, because the file is edited by\n * hand and \"something in it is wrong\" is not a fix. Fail-closed throughout —\n * an unknown key, a non-string entry, a malformed call all refuse before the\n * caller writes a single byte; a partially-understood fleet file must never\n * become a partially-right landscape. The node-level readers live in\n * `./items.ts`; this module owns what their answers must amount to.\n *\n * The grammars are the shared ones and never a private copy: service ids\n * through `parseServiceId`, subsystem names through `parseSubsystemName`,\n * externals through the same `dirNameHazard` those two are built on (an\n * external never becomes a directory, but calls name services and externals\n * out of ONE namespace, and a name legal as one and illegal as the other\n * would make the duplicate refusal incoherent — `kernel/ids/service.ts`'s own\n * argument, one level up).\n */\nimport { isMap, isScalar, LineCounter, parseDocument, type Pair } from \"yaml\";\nimport { dirNameHazard, parseServiceId, type ServiceId } from \"../../kernel/ids/service.js\";\nimport { parseSubsystemName, type SubsystemName } from \"../../kernel/ids/subsystem.js\";\nimport { at, hint, listOf, serviceEntry, stringOf, type SeedCtx } from \"./items.js\";\n\nexport interface SeedService {\n readonly id: ServiceId;\n /** The subsystem this service is filed under, or null for unfiled. */\n readonly subsystem: SubsystemName | null;\n}\n\n/** One `a -> b` line. Endpoints are declared names (service ids or externals), not element ids. */\nexport interface SeedCall {\n readonly from: string;\n readonly to: string;\n}\n\n/** The whole fleet file, validated: what the templater renders from. */\nexport interface FleetSeed {\n readonly services: readonly SeedService[];\n readonly subsystems: readonly SubsystemName[];\n readonly externals: readonly string[];\n readonly calls: readonly SeedCall[];\n}\n\n/**\n * Why the file was refused, discriminated by the ACTION the caller takes —\n * each kind maps to one stable error code at the command layer (`invalid` →\n * `seed-file-invalid`, `duplicate` → `seed-duplicate-service`,\n * `unknown-subsystem` → `seed-unknown-subsystem`, `unknown-endpoint` → the\n * existing `unknown-service`, because the fix is the same as for every other\n * unknown service: correct the name and re-run).\n */\nexport type SeedFileProblem =\n | { readonly kind: \"invalid\"; readonly message: string }\n | { readonly kind: \"duplicate\"; readonly message: string }\n | { readonly kind: \"unknown-subsystem\"; readonly message: string }\n | { readonly kind: \"unknown-endpoint\"; readonly message: string };\n\nexport type FleetFileRead =\n | { readonly ok: true; readonly seed: FleetSeed }\n | { readonly ok: false; readonly problem: SeedFileProblem };\n\nconst TOP_KEYS = [\"services\", \"subsystems\", \"externals\", \"calls\"] as const;\n\nfunction invalid(message: string): FleetFileRead {\n return { ok: false, problem: { kind: \"invalid\", message } };\n}\n\n/** Parse and validate the fleet file's text. Never throws for an expected outcome. */\nexport function readFleetFile(text: string, file: string): FleetFileRead {\n const lines = new LineCounter();\n const doc = parseDocument(text, { lineCounter: lines });\n const ctx: SeedCtx = { file, lines };\n const parseError = doc.errors[0];\n if (parseError !== undefined) {\n const line = parseError.linePos?.[0]?.line;\n return invalid(\n `${file}${line === undefined ? \"\" : `:${line}`} cannot be read as YAML — ` +\n `${parseError.message.split(\"\\n\")[0]!}`,\n );\n }\n if (!isMap(doc.contents)) {\n return invalid(\n `${file} must be a YAML mapping with a \\`services:\\` list (and optional ` +\n `\\`subsystems:\\`, \\`externals:\\`, \\`calls:\\`) — the whole document is ` +\n `${doc.contents === null ? \"empty\" : \"not a mapping\"}.`,\n );\n }\n\n // The top level, fail-closed: a mistyped key is a whole section silently\n // dropped, which is exactly the partial scaffold this reader must refuse.\n const sections = new Map<string, unknown>();\n for (const pair of doc.contents.items as Pair[]) {\n const key = isScalar(pair.key) && typeof pair.key.value === \"string\" ? pair.key.value : null;\n if (key === null || !(TOP_KEYS as readonly string[]).includes(key)) {\n const spelled = key ?? String(pair.key);\n return invalid(\n `${at(ctx, pair.key)} — unknown key '${spelled}': the fleet file's keys are ` +\n `services, subsystems, externals and calls.${hint(spelled, TOP_KEYS)}`,\n );\n }\n sections.set(key, pair.value);\n }\n\n // One flat namespace over everything a name can be declared as, because a\n // call endpoint must name exactly one thing — the same rule the tree walk\n // enforces over `services/` (`subsystem.name-collision`).\n const declared = new Map<string, { where: string; role: string }>();\n const claim = (name: string, where: string, role: string): SeedFileProblem | null => {\n const held = declared.get(name);\n if (held !== undefined) {\n return {\n kind: \"duplicate\",\n message:\n `${where} declares '${name}' as ${role}, but ${held.where} already declares it as ` +\n `${held.role} — service ids, subsystem names and externals share one flat namespace ` +\n `(a call endpoint must name exactly one thing). Rename or remove one, then re-run.`,\n };\n }\n declared.set(name, { where, role });\n return null;\n };\n\n const subsystems: SubsystemName[] = [];\n for (const item of listOf(sections.get(\"subsystems\"))) {\n const raw = stringOf(item);\n if (raw === null) return invalid(`${at(ctx, item)} — a subsystems entry must be a string (quote it if YAML reads it as something else).`);\n const parsed = parseSubsystemName(raw, \"subsystems entry\");\n if (!parsed.ok) return invalid(`${at(ctx, item)} — ${parsed.problem}`);\n const dup = claim(raw, at(ctx, item), \"a subsystem\");\n if (dup !== null) return { ok: false, problem: dup };\n subsystems.push(parsed.name);\n }\n\n const services: SeedService[] = [];\n /** Deferred so `subsystem: X` can reference a subsystem declared lower in the file. */\n const subsystemRefs: { id: ServiceId; sub: string; where: string }[] = [];\n for (const item of listOf(sections.get(\"services\"))) {\n const entry = serviceEntry(ctx, item);\n if (\"invalid\" in entry) return invalid(entry.invalid);\n const parsed = parseServiceId(entry.id, \"services entry\");\n if (!parsed.ok) return invalid(`${entry.where} — ${parsed.problem}`);\n const dup = claim(entry.id, entry.where, \"a service\");\n if (dup !== null) return { ok: false, problem: dup };\n if (entry.subsystem !== null) subsystemRefs.push({ id: parsed.id, sub: entry.subsystem, where: entry.where });\n services.push({ id: parsed.id, subsystem: null });\n }\n if (services.length === 0) {\n return invalid(\n `${file} declares no services — \\`services:\\` is the one required key: list every ` +\n `service id the fleet has (a bare string, or \\`{ id, subsystem }\\` to file one under a group).`,\n );\n }\n\n const externals: string[] = [];\n for (const item of listOf(sections.get(\"externals\"))) {\n const raw = stringOf(item);\n if (raw === null) return invalid(`${at(ctx, item)} — an externals entry must be a string (quote it if YAML reads it as something else).`);\n if (dirNameHazard(raw) !== null) {\n return invalid(\n `${at(ctx, item)} — '${raw}' cannot be an external's name: externals share the service-id ` +\n `grammar (start with a letter or digit; letters, digits, '.', '_', '-'; no Windows-reserved ` +\n `names), because calls name services and externals out of one namespace.`,\n );\n }\n const dup = claim(raw, at(ctx, item), \"an external\");\n if (dup !== null) return { ok: false, problem: dup };\n externals.push(raw);\n }\n\n // Subsystem references, now that every declaration is in: a `subsystem:`\n // naming nothing is the second-likeliest typo in the file, and the hint\n // offers only names the file really declares.\n const subNames = new Set<string>(subsystems);\n for (const ref of subsystemRefs) {\n if (!subNames.has(ref.sub)) {\n return {\n ok: false,\n problem: {\n kind: \"unknown-subsystem\",\n message:\n `${ref.where} files service '${ref.id}' under '${ref.sub}', but \\`subsystems:\\` ` +\n `declares no '${ref.sub}'.${hint(ref.sub, subsystems)} Add it to subsystems:, ` +\n `or fix the spelling, then re-run.`,\n },\n };\n }\n }\n // The branded name comes from the DECLARATION list, never from a second\n // parse of the reference: `subsystems:` is where the grammar ran, and the\n // membership check above proved every reference resolves into it.\n const bySubName = new Map<string, SubsystemName>(subsystems.map((s) => [s as string, s]));\n const filed = new Map(subsystemRefs.map((r) => [r.id as string, bySubName.get(r.sub) ?? null]));\n const seededServices: SeedService[] = services.map((s) => ({\n id: s.id,\n subsystem: filed.get(s.id) ?? null,\n }));\n\n const endpointProblem = (raw: string, name: string, where: string): SeedFileProblem => {\n if (subNames.has(name)) {\n return {\n kind: \"unknown-endpoint\",\n message:\n `${where} — call '${raw}' names the subsystem '${name}': a subsystem is a place ` +\n `services live, never a call endpoint. Name one of its services instead.`,\n };\n }\n return {\n kind: \"unknown-endpoint\",\n message:\n `${where} — call '${raw}' names '${name}', which no services: or externals: entry ` +\n `declares.${hint(name, [...services.map((s) => s.id), ...externals])} Declare it, ` +\n `or fix the spelling, then re-run.`,\n };\n };\n const callable = new Set<string>([...services.map((s) => s.id), ...externals]);\n const calls: SeedCall[] = [];\n const seenCalls = new Set<string>();\n for (const item of listOf(sections.get(\"calls\"))) {\n const raw = stringOf(item);\n if (raw === null) return invalid(`${at(ctx, item)} — a calls entry must be a string of the form 'caller -> callee'.`);\n const m = /^(\\S+)\\s*->\\s*(\\S+)$/.exec(raw.trim());\n if (m === null) {\n return invalid(`${at(ctx, item)} — a call is written 'caller -> callee' (got '${raw}').`);\n }\n const [from, to] = [m[1]!, m[2]!];\n for (const end of [from, to]) {\n if (!callable.has(end)) return { ok: false, problem: endpointProblem(raw, end, at(ctx, item)) };\n }\n // LikeC4 has no self-edge: it reads `a -> a` as a parent-child\n // relationship and refuses the whole file. Caught HERE, with the line,\n // because the alternative is the self-check refusing `internal` over a\n // fleet file whose fault it is not — and a copy-paste inside `calls:` is\n // exactly how this line gets written.\n if (from === to) {\n return invalid(\n `${at(ctx, item)} — call '${raw}' names '${from}' at both ends, and LikeC4 has no ` +\n `self-edge (it reads 'a -> a' as a parent-child relationship). Name the other ` +\n `endpoint, or drop the line.`,\n );\n }\n const key = `${from} ${to}`;\n // A repeated line is the same fact stated twice, not a second edge.\n if (seenCalls.has(key)) continue;\n seenCalls.add(key);\n calls.push({ from, to });\n }\n\n return { ok: true, seed: { services: seededServices, subsystems, externals, calls } };\n}\n"]}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One fleet.yaml item, read off the AST: the section-item and scalar readers,
|
|
3
|
+
* the `file:line` addressing every refusal opens with, and the one composite
|
|
4
|
+
* entry shape (`services:`'s string-or-mapping item). Split from
|
|
5
|
+
* `./fleet-file.ts` on the phase seam — this module answers "what does this
|
|
6
|
+
* NODE say, and where is it", while fleet-file owns what the answers must
|
|
7
|
+
* amount to (grammar, the flat namespace, reference resolution).
|
|
8
|
+
*
|
|
9
|
+
* `serviceEntry` reports its refusals as a plain `invalid` sentence rather
|
|
10
|
+
* than the reader's discriminated problem union, deliberately: every shape
|
|
11
|
+
* failure at item level maps to `seed-file-invalid`, and returning the
|
|
12
|
+
* sentence keeps this module free of the domain union — the import points
|
|
13
|
+
* fleet-file → items only, never back.
|
|
14
|
+
*/
|
|
15
|
+
import { isMap, isScalar, isSeq } from "yaml";
|
|
16
|
+
import { closeIds } from "../arch.js";
|
|
17
|
+
/** `file:line` for a node, or just the file when the node carries no range. */
|
|
18
|
+
export function at(ctx, node) {
|
|
19
|
+
const range = node.range;
|
|
20
|
+
if (range === undefined || range === null)
|
|
21
|
+
return ctx.file;
|
|
22
|
+
return `${ctx.file}:${ctx.lines.linePos(range[0]).line}`;
|
|
23
|
+
}
|
|
24
|
+
/** A did-you-mean clause over real names, or the empty string. */
|
|
25
|
+
export function hint(name, pool) {
|
|
26
|
+
const close = closeIds(name, [...pool]);
|
|
27
|
+
return close.length === 0 ? "" : ` Did you mean ${close.map((c) => `'${c}'`).join(" or ")}?`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A section's items. An absent key and a key with nothing under it are both
|
|
31
|
+
* the empty list — clearing out the last entry must not require deleting the
|
|
32
|
+
* heading too. A non-sequence value comes back as ONE item, which reads
|
|
33
|
+
* `services: checkout` as the one-entry list it plainly means and hands
|
|
34
|
+
* everything else — a number, a mapping under `calls:` — to the caller's
|
|
35
|
+
* per-item type check, which refuses it with the node's own line.
|
|
36
|
+
*/
|
|
37
|
+
export function listOf(node) {
|
|
38
|
+
if (node === undefined || node === null)
|
|
39
|
+
return [];
|
|
40
|
+
// `parseDocument` gives a heading with nothing under it a Scalar HOLDING
|
|
41
|
+
// null, never a bare null, so the emptied-out section only looks like the
|
|
42
|
+
// absent one above. Without this arm, deleting the last entry under
|
|
43
|
+
// `externals:` refuses with "an externals entry must be a string (quote it
|
|
44
|
+
// if YAML reads it as something else)" — advice about a value that is not
|
|
45
|
+
// there — and an emptied `services:` shadows the friendly "declares no
|
|
46
|
+
// services" sentence that exists for exactly that case.
|
|
47
|
+
if (isScalar(node) && node.value === null)
|
|
48
|
+
return [];
|
|
49
|
+
if (isSeq(node))
|
|
50
|
+
return node.items;
|
|
51
|
+
return [node];
|
|
52
|
+
}
|
|
53
|
+
/** A scalar's string value, or null when the entry is not a string. */
|
|
54
|
+
export function stringOf(item) {
|
|
55
|
+
if (!isScalar(item) || typeof item.value !== "string")
|
|
56
|
+
return null;
|
|
57
|
+
return item.value;
|
|
58
|
+
}
|
|
59
|
+
/** One services entry: a bare string id, or `{ id, subsystem? }` — nothing else. */
|
|
60
|
+
export function serviceEntry(ctx, item) {
|
|
61
|
+
const where = at(ctx, item);
|
|
62
|
+
const bare = stringOf(item);
|
|
63
|
+
if (bare !== null)
|
|
64
|
+
return { id: bare, subsystem: null, where };
|
|
65
|
+
if (!isMap(item)) {
|
|
66
|
+
return { invalid: `${where} — a services entry is a string id, or a mapping with \`id\` and optional \`subsystem\`.` };
|
|
67
|
+
}
|
|
68
|
+
let id = null;
|
|
69
|
+
let subsystem = null;
|
|
70
|
+
for (const pair of item.items) {
|
|
71
|
+
const key = isScalar(pair.key) && typeof pair.key.value === "string" ? pair.key.value : null;
|
|
72
|
+
const value = stringOf(pair.value);
|
|
73
|
+
if (key === "id" && value !== null)
|
|
74
|
+
id = value;
|
|
75
|
+
else if (key === "subsystem" && value !== null)
|
|
76
|
+
subsystem = value;
|
|
77
|
+
else {
|
|
78
|
+
const spelled = key ?? String(pair.key);
|
|
79
|
+
return {
|
|
80
|
+
invalid: `${at(ctx, pair.key)} — a services entry takes \`id\` and optional \`subsystem\` ` +
|
|
81
|
+
`(both strings); '${spelled}' is ${key === "id" || key === "subsystem" ? "not a string here" : "not one of them"}.` +
|
|
82
|
+
`${hint(spelled, ["id", "subsystem"])}`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (id === null) {
|
|
87
|
+
return { invalid: `${where} — a services mapping entry must carry \`id\`.` };
|
|
88
|
+
}
|
|
89
|
+
return { id, subsystem, where };
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=items.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"items.js","sourceRoot":"","sources":["../../../../src/core/c4/seed/items.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One fleet.yaml item, read off the AST: the section-item and scalar readers,\n * the `file:line` addressing every refusal opens with, and the one composite\n * entry shape (`services:`'s string-or-mapping item). Split from\n * `./fleet-file.ts` on the phase seam — this module answers \"what does this\n * NODE say, and where is it\", while fleet-file owns what the answers must\n * amount to (grammar, the flat namespace, reference resolution).\n *\n * `serviceEntry` reports its refusals as a plain `invalid` sentence rather\n * than the reader's discriminated problem union, deliberately: every shape\n * failure at item level maps to `seed-file-invalid`, and returning the\n * sentence keeps this module free of the domain union — the import points\n * fleet-file → items only, never back.\n */\nimport { isMap, isScalar, isSeq, type LineCounter, type Pair } from \"yaml\";\nimport { closeIds } from \"../arch.js\";\n\n/** What the readers walk with: the display path and the offset→line map. */\nexport interface SeedCtx {\n readonly file: string;\n readonly lines: LineCounter;\n}\n\n/** `file:line` for a node, or just the file when the node carries no range. */\nexport function at(ctx: SeedCtx, node: unknown): string {\n const range = (node as { range?: readonly [number, number, number] | null }).range;\n if (range === undefined || range === null) return ctx.file;\n return `${ctx.file}:${ctx.lines.linePos(range[0]).line}`;\n}\n\n/** A did-you-mean clause over real names, or the empty string. */\nexport function hint(name: string, pool: readonly string[]): string {\n const close = closeIds(name, [...pool]);\n return close.length === 0 ? \"\" : ` Did you mean ${close.map((c) => `'${c}'`).join(\" or \")}?`;\n}\n\n/**\n * A section's items. An absent key and a key with nothing under it are both\n * the empty list — clearing out the last entry must not require deleting the\n * heading too. A non-sequence value comes back as ONE item, which reads\n * `services: checkout` as the one-entry list it plainly means and hands\n * everything else — a number, a mapping under `calls:` — to the caller's\n * per-item type check, which refuses it with the node's own line.\n */\nexport function listOf(node: unknown): unknown[] {\n if (node === undefined || node === null) return [];\n // `parseDocument` gives a heading with nothing under it a Scalar HOLDING\n // null, never a bare null, so the emptied-out section only looks like the\n // absent one above. Without this arm, deleting the last entry under\n // `externals:` refuses with \"an externals entry must be a string (quote it\n // if YAML reads it as something else)\" — advice about a value that is not\n // there — and an emptied `services:` shadows the friendly \"declares no\n // services\" sentence that exists for exactly that case.\n if (isScalar(node) && node.value === null) return [];\n if (isSeq(node)) return node.items;\n return [node];\n}\n\n/** A scalar's string value, or null when the entry is not a string. */\nexport function stringOf(item: unknown): string | null {\n if (!isScalar(item) || typeof item.value !== \"string\") return null;\n return item.value;\n}\n\nexport type ServiceEntryRead =\n | { id: string; subsystem: string | null; where: string }\n | { invalid: string };\n\n/** One services entry: a bare string id, or `{ id, subsystem? }` — nothing else. */\nexport function serviceEntry(ctx: SeedCtx, item: unknown): ServiceEntryRead {\n const where = at(ctx, item);\n const bare = stringOf(item);\n if (bare !== null) return { id: bare, subsystem: null, where };\n if (!isMap(item)) {\n return { invalid: `${where} — a services entry is a string id, or a mapping with \\`id\\` and optional \\`subsystem\\`.` };\n }\n let id: string | null = null;\n let subsystem: string | null = null;\n for (const pair of item.items as Pair[]) {\n const key = isScalar(pair.key) && typeof pair.key.value === \"string\" ? pair.key.value : null;\n const value = stringOf(pair.value);\n if (key === \"id\" && value !== null) id = value;\n else if (key === \"subsystem\" && value !== null) subsystem = value;\n else {\n const spelled = key ?? String(pair.key);\n return {\n invalid:\n `${at(ctx, pair.key)} — a services entry takes \\`id\\` and optional \\`subsystem\\` ` +\n `(both strings); '${spelled}' is ${key === \"id\" || key === \"subsystem\" ? \"not a string here\" : \"not one of them\"}.` +\n `${hint(spelled, [\"id\", \"subsystem\"])}`,\n };\n }\n }\n if (id === null) {\n return { invalid: `${where} — a services mapping entry must carry \\`id\\`.` };\n }\n return { id, subsystem, where };\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The generated-by stamp on a seeded landscape: line 1, self-verifying, no
|
|
3
|
+
* state anywhere else. `loam seed` may regenerate the fleet map wholesale
|
|
4
|
+
* exactly when it can PROVE no human work would be lost, and the only proof
|
|
5
|
+
* that needs no side channel is the file testifying about itself — a digest of
|
|
6
|
+
* every byte after the stamp line, written into the stamp line. A database of
|
|
7
|
+
* "files loam wrote" would be a second source of truth that drifts the first
|
|
8
|
+
* time somebody copies a docs repo; the digest travels with the bytes it
|
|
9
|
+
* covers.
|
|
10
|
+
*
|
|
11
|
+
* The stamp is recognised by its PREFIX and digest alone — the prose after the
|
|
12
|
+
* digest is free to be reworded (it is advice to a reader, not part of the
|
|
13
|
+
* proof), so a future loam rewording the sentence still recognises every file
|
|
14
|
+
* an older loam sealed. The digest covers everything after the first newline,
|
|
15
|
+
* with line endings normalised out: `core.autocrlf` is Git for Windows'
|
|
16
|
+
* installer default and the docs repo ships no `.gitattributes`, so an
|
|
17
|
+
* ordinary Windows clone of a seeded landscape rewrites every line of it and
|
|
18
|
+
* changes not one fact. Calling that "hand-edited" would stand `loam seed`
|
|
19
|
+
* against `loam status`, which reads the same file through
|
|
20
|
+
* `isLandscapeStub`'s CRLF-normalised compare and tells the reader to run
|
|
21
|
+
* seed. What the digest proves is that the CONTENT is still a pure function of
|
|
22
|
+
* fleet.yaml — which is what "no human work here" means.
|
|
23
|
+
*/
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
25
|
+
const STAMP_PREFIX = "// loam-seed sha256:";
|
|
26
|
+
/** Line 1 must OPEN with the prefix and a full digest; the prose after is free. */
|
|
27
|
+
const STAMP_LINE = /^\/\/ loam-seed sha256:([0-9a-f]{64})(?:\s|$)/;
|
|
28
|
+
/** The digest the stamp carries: content, with line endings normalised out. See the header. */
|
|
29
|
+
function digestOf(body) {
|
|
30
|
+
return createHash("sha256").update(body.replace(/\r\n/g, "\n"), "utf8").digest("hex");
|
|
31
|
+
}
|
|
32
|
+
/** Prepend the self-verifying stamp line to a rendered landscape body. */
|
|
33
|
+
export function sealLandscape(body) {
|
|
34
|
+
return (`${STAMP_PREFIX}${digestOf(body)} — generated from fleet.yaml by \`loam seed\`. ` +
|
|
35
|
+
"Hand edits make this file yours: seed will then refuse to regenerate it.\n" +
|
|
36
|
+
body);
|
|
37
|
+
}
|
|
38
|
+
/** Classify a landscape's text by its own testimony. See {@link LandscapeProvenance}. */
|
|
39
|
+
export function landscapeProvenance(text) {
|
|
40
|
+
const newline = text.indexOf("\n");
|
|
41
|
+
const first = newline === -1 ? text : text.slice(0, newline);
|
|
42
|
+
const m = STAMP_LINE.exec(first);
|
|
43
|
+
if (m === null)
|
|
44
|
+
return "foreign";
|
|
45
|
+
const body = newline === -1 ? "" : text.slice(newline + 1);
|
|
46
|
+
return digestOf(body) === m[1] ? "seed-stamped" : "seed-edited";
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=stamp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stamp.js","sourceRoot":"","sources":["../../../../src/core/c4/seed/stamp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAezC,MAAM,YAAY,GAAG,sBAAsB,CAAC;AAE5C,mFAAmF;AACnF,MAAM,UAAU,GAAG,+CAA+C,CAAC;AAEnE,+FAA+F;AAC/F,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACxF,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,CACL,GAAG,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,iDAAiD;QACjF,4EAA4E;QAC5E,IAAI,CACL,CAAC;AACJ,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC7D,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;IAC3D,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC;AAClE,CAAC","sourcesContent":["/**\n * The generated-by stamp on a seeded landscape: line 1, self-verifying, no\n * state anywhere else. `loam seed` may regenerate the fleet map wholesale\n * exactly when it can PROVE no human work would be lost, and the only proof\n * that needs no side channel is the file testifying about itself — a digest of\n * every byte after the stamp line, written into the stamp line. A database of\n * \"files loam wrote\" would be a second source of truth that drifts the first\n * time somebody copies a docs repo; the digest travels with the bytes it\n * covers.\n *\n * The stamp is recognised by its PREFIX and digest alone — the prose after the\n * digest is free to be reworded (it is advice to a reader, not part of the\n * proof), so a future loam rewording the sentence still recognises every file\n * an older loam sealed. The digest covers everything after the first newline,\n * with line endings normalised out: `core.autocrlf` is Git for Windows'\n * installer default and the docs repo ships no `.gitattributes`, so an\n * ordinary Windows clone of a seeded landscape rewrites every line of it and\n * changes not one fact. Calling that \"hand-edited\" would stand `loam seed`\n * against `loam status`, which reads the same file through\n * `isLandscapeStub`'s CRLF-normalised compare and tells the reader to run\n * seed. What the digest proves is that the CONTENT is still a pure function of\n * fleet.yaml — which is what \"no human work here\" means.\n */\nimport { createHash } from \"node:crypto\";\n\n/**\n * What the landscape's bytes say about who wrote them:\n *\n * - `seed-stamped` — the stamp is present and its digest matches: every byte\n * is a pure function of fleet.yaml, so regenerating loses nothing.\n * - `seed-edited` — the stamp is present but the digest does not match: a\n * human edited a seeded file, and those edits are theirs to keep.\n * - `foreign` — no stamp: the file was authored some other way (by hand, or by\n * the scaffold; the caller recognises the scaffold's stub separately, through\n * `isLandscapeStub`, because the stub predates the stamp).\n */\nexport type LandscapeProvenance = \"seed-stamped\" | \"seed-edited\" | \"foreign\";\n\nconst STAMP_PREFIX = \"// loam-seed sha256:\";\n\n/** Line 1 must OPEN with the prefix and a full digest; the prose after is free. */\nconst STAMP_LINE = /^\\/\\/ loam-seed sha256:([0-9a-f]{64})(?:\\s|$)/;\n\n/** The digest the stamp carries: content, with line endings normalised out. See the header. */\nfunction digestOf(body: string): string {\n return createHash(\"sha256\").update(body.replace(/\\r\\n/g, \"\\n\"), \"utf8\").digest(\"hex\");\n}\n\n/** Prepend the self-verifying stamp line to a rendered landscape body. */\nexport function sealLandscape(body: string): string {\n return (\n `${STAMP_PREFIX}${digestOf(body)} — generated from fleet.yaml by \\`loam seed\\`. ` +\n \"Hand edits make this file yours: seed will then refuse to regenerate it.\\n\" +\n body\n );\n}\n\n/** Classify a landscape's text by its own testimony. See {@link LandscapeProvenance}. */\nexport function landscapeProvenance(text: string): LandscapeProvenance {\n const newline = text.indexOf(\"\\n\");\n const first = newline === -1 ? text : text.slice(0, newline);\n const m = STAMP_LINE.exec(first);\n if (m === null) return \"foreign\";\n const body = newline === -1 ? \"\" : text.slice(newline + 1);\n return digestOf(body) === m[1] ? \"seed-stamped\" : \"seed-edited\";\n}\n"]}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A LikeC4 identifier for a declared name, mechanically: `.` and `-` become
|
|
3
|
+
* `_`, and the result takes its kind's prefix — `svc_` for a service, `ext_`
|
|
4
|
+
* for an external.
|
|
5
|
+
*
|
|
6
|
+
* The prefix is not decoration, and not only about the digit-initial case
|
|
7
|
+
* (LikeC4 identifiers are letter/underscore-initial). LikeC4's KEYWORDS are
|
|
8
|
+
* bare lowercase words — `notes`, `tag`, `view`, `metadata`, `style`, `link`,
|
|
9
|
+
* `import`, `description`, `size`, `line`, `this` and some thirty more — and a
|
|
10
|
+
* service legally named `notes` emitted as a bare `notes = softwareSystem`
|
|
11
|
+
* does not parse. The self-check would catch that, but only to refuse a fleet
|
|
12
|
+
* whose fault it is not, under `internal`, with no fix to offer. No keyword
|
|
13
|
+
* contains `_`, so a prefixed identifier is PROVABLY not one — at this LikeC4
|
|
14
|
+
* version and at every later one. A reserved-word list here would be the same
|
|
15
|
+
* bug again, rotting the first time the language grew a word.
|
|
16
|
+
*
|
|
17
|
+
* The fold is not injective — `a-b` and `a.b` both give `a_b` — so residual
|
|
18
|
+
* collisions take a deterministic numeric suffix in sorted processing order.
|
|
19
|
+
* Aesthetics do not matter here: the binding metadata, not the element id, is
|
|
20
|
+
* the element's identity to loam.
|
|
21
|
+
*/
|
|
22
|
+
function foldName(name, kind) {
|
|
23
|
+
return `${kind}_${name.replace(/[.-]/g, "_")}`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The element id loam SUGGESTS for a service no map draws yet — this seed's
|
|
27
|
+
* own convention, exported so it is the only one. The adopt brief and the
|
|
28
|
+
* `loam new` delta used to camel-case the directory name instead, so a seeded
|
|
29
|
+
* fleet received three spellings of one box (`svc_payment_service` on the
|
|
30
|
+
* map, `paymentService` in every brief and scaffold), and a delta that reused
|
|
31
|
+
* the scaffold's guess never lined up with the map it was told to match. The
|
|
32
|
+
* id stays cosmetic — `metadata { service '<id>' }` is the join — but a
|
|
33
|
+
* suggestion gets pasted, so it has to be one spelling, and the keyword-proof
|
|
34
|
+
* one above is the one already on disk in every seeded repo.
|
|
35
|
+
*/
|
|
36
|
+
export function serviceElementId(service) {
|
|
37
|
+
return foldName(service, "svc");
|
|
38
|
+
}
|
|
39
|
+
/** Element ids for every declared name (services ∪ externals), collision-suffixed deterministically. */
|
|
40
|
+
function elementIds(seed) {
|
|
41
|
+
const ids = new Map();
|
|
42
|
+
const used = new Set();
|
|
43
|
+
const named = [
|
|
44
|
+
...[...seed.services]
|
|
45
|
+
.map((s) => s.id)
|
|
46
|
+
.sort()
|
|
47
|
+
.map((n) => [n, "svc"]),
|
|
48
|
+
...[...seed.externals].sort().map((n) => [n, "ext"]),
|
|
49
|
+
];
|
|
50
|
+
for (const [name, kind] of named) {
|
|
51
|
+
let candidate = foldName(name, kind);
|
|
52
|
+
for (let n = 2; used.has(candidate); n += 1)
|
|
53
|
+
candidate = `${foldName(name, kind)}_${n}`;
|
|
54
|
+
used.add(candidate);
|
|
55
|
+
ids.set(name, candidate);
|
|
56
|
+
}
|
|
57
|
+
return ids;
|
|
58
|
+
}
|
|
59
|
+
/** Render the landscape body (unstamped — `sealLandscape` adds line 1). */
|
|
60
|
+
export function renderLandscape(seed) {
|
|
61
|
+
const ids = elementIds(seed);
|
|
62
|
+
// Every name reaching `el` was declared (calls are validated against
|
|
63
|
+
// services ∪ externals before rendering), so the fallback is unreachable —
|
|
64
|
+
// it exists so a future caller cannot get a silent `undefined` into the DSL.
|
|
65
|
+
const el = (name) => ids.get(name) ?? foldName(name, "svc");
|
|
66
|
+
const lines = [
|
|
67
|
+
"// Generated from fleet.yaml by `loam seed` — edit fleet.yaml and re-run to",
|
|
68
|
+
"// regenerate. Hand edits make this file yours: the stamp on line 1 is how",
|
|
69
|
+
"// seed tells, and it will then refuse to touch the file again.",
|
|
70
|
+
"//",
|
|
71
|
+
"// As contracts arrive, give each call the operationId it uses, so",
|
|
72
|
+
"// requirements, C4 and OpenAPI can be cross-checked:",
|
|
73
|
+
"//",
|
|
74
|
+
"// svc_checkout -> svc_payment_service 'Calls authorizePayment' {",
|
|
75
|
+
"// metadata { op 'authorizePayment' }",
|
|
76
|
+
"// }",
|
|
77
|
+
"",
|
|
78
|
+
"specification {",
|
|
79
|
+
" element person",
|
|
80
|
+
" element softwareSystem",
|
|
81
|
+
" element container",
|
|
82
|
+
" element database",
|
|
83
|
+
"",
|
|
84
|
+
" // #platform keeps ubiquitous infrastructure out of the fleet view;",
|
|
85
|
+
" // #external marks somebody else's system, so validate does not ask for a",
|
|
86
|
+
" // services/ directory nobody owes. A tag must be declared before it is",
|
|
87
|
+
" // used, so both are declared here whether or not this fleet uses them.",
|
|
88
|
+
" tag platform",
|
|
89
|
+
" tag external",
|
|
90
|
+
// The relationship kinds are DUPLICATED from core/scaffold/landscape.ts's
|
|
91
|
+
// LANDSCAPE_STUB for the views block's reason below, and test/seed.test.ts
|
|
92
|
+
// compares the two byte for byte the same way. Declared, never used: the
|
|
93
|
+
// calls below stay plain edges, because fleet.yaml says who calls whom and
|
|
94
|
+
// has no spelling for what an edge carries — choosing a kind here would be
|
|
95
|
+
// the guess this templater exists not to make. Declaring them is what lets
|
|
96
|
+
// the adoptions and feature deltas that follow draw kinded edges into this
|
|
97
|
+
// map, and lets an archive that copies one in still parse.
|
|
98
|
+
"",
|
|
99
|
+
" // One kind per kind of traffic, each with one direction. The style is the",
|
|
100
|
+
" // picture: amber for messages, green for data at rest, gray and dashed for",
|
|
101
|
+
" // a connection nobody has given a direction yet. Never tag a kind: LikeC4",
|
|
102
|
+
" // copies a kind's tags onto every edge of it.",
|
|
103
|
+
" relationship publishes {",
|
|
104
|
+
" color amber",
|
|
105
|
+
" line solid",
|
|
106
|
+
" head normal",
|
|
107
|
+
" }",
|
|
108
|
+
" relationship delivers {",
|
|
109
|
+
" color amber",
|
|
110
|
+
" line solid",
|
|
111
|
+
" head normal",
|
|
112
|
+
" }",
|
|
113
|
+
" relationship reads {",
|
|
114
|
+
" color green",
|
|
115
|
+
" head none",
|
|
116
|
+
" tail normal",
|
|
117
|
+
" }",
|
|
118
|
+
" relationship writes {",
|
|
119
|
+
" color green",
|
|
120
|
+
" head normal",
|
|
121
|
+
" }",
|
|
122
|
+
" relationship readsWrites {",
|
|
123
|
+
" color green",
|
|
124
|
+
" head normal",
|
|
125
|
+
" tail normal",
|
|
126
|
+
" }",
|
|
127
|
+
" relationship calls {",
|
|
128
|
+
" color secondary",
|
|
129
|
+
" line solid",
|
|
130
|
+
" head normal",
|
|
131
|
+
" }",
|
|
132
|
+
" relationship connects {",
|
|
133
|
+
" color gray",
|
|
134
|
+
" line dashed",
|
|
135
|
+
" head none",
|
|
136
|
+
" // A marker at the source: an edge with no arrowhead at either end is laid",
|
|
137
|
+
" // out unconstrained, and enough of them flatten a view into one band.",
|
|
138
|
+
" tail odot",
|
|
139
|
+
" }",
|
|
140
|
+
"}",
|
|
141
|
+
"",
|
|
142
|
+
"model {",
|
|
143
|
+
];
|
|
144
|
+
const services = [...seed.services].sort((a, b) => (a.id < b.id ? -1 : 1));
|
|
145
|
+
for (const s of services) {
|
|
146
|
+
lines.push(` ${el(s.id)} = softwareSystem '${s.id}' {`);
|
|
147
|
+
lines.push(` metadata { service '${s.id}' }`);
|
|
148
|
+
lines.push(" }");
|
|
149
|
+
}
|
|
150
|
+
const externals = [...seed.externals].sort();
|
|
151
|
+
for (const x of externals) {
|
|
152
|
+
lines.push(` ${el(x)} = softwareSystem '${x}' {`);
|
|
153
|
+
lines.push(" #external");
|
|
154
|
+
lines.push(" }");
|
|
155
|
+
}
|
|
156
|
+
const calls = [...seed.calls].sort((a, b) => a.from === b.from ? (a.to < b.to ? -1 : 1) : a.from < b.from ? -1 : 1);
|
|
157
|
+
if (calls.length > 0)
|
|
158
|
+
lines.push("");
|
|
159
|
+
for (const c of calls)
|
|
160
|
+
lines.push(` ${el(c.from)} -> ${el(c.to)}`);
|
|
161
|
+
lines.push("}");
|
|
162
|
+
// The views block below is DUPLICATED from core/docs.ts's LANDSCAPE_STUB
|
|
163
|
+
// (two copies — extract on a third, per the house rule). The tripwire in
|
|
164
|
+
// test/seed.test.ts compares the two blocks BYTE FOR BYTE, comments and
|
|
165
|
+
// wrapping included, so whichever copy drifts fails the suite — a
|
|
166
|
+
// view-names-only compare let a corrected predicate land in one copy and
|
|
167
|
+
// leave every seeded fleet map rendering the old one.
|
|
168
|
+
// The emitted comment speaks to the docs repo's reader, not to loam's:
|
|
169
|
+
// internal paths mean nothing where this file lands.
|
|
170
|
+
lines.push("", "// The views are for LikeC4's own tooling — loam reads the model and never", "// a view. Render with `npx likec4 start` from the docs repo root. Tag", "// ubiquitous infrastructure #platform to keep the fleet view readable.", "views {", " view fleet {", " include *", " exclude element.tag = #platform", " }", " // The obvious spelling — `include element.tag = #platform` — draws the", " // platform boxes with NO edges and no consumers. The predicate that works is", " // the relationship form below.", " view platform {", " include * -> element.tag = #platform", " }", "}");
|
|
171
|
+
return lines.join("\n") + "\n";
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../../../src/core/c4/seed/template.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * FleetSeed → landscape DSL body: a pure, deterministic function — identical\n * fleet.yaml, identical bytes, on any machine — because the stamp\n * (`./stamp.ts`) digests the output and a nondeterministic templater would\n * read as hand-edited on the next run.\n *\n * The DSL shape is deliberately minimal: one bound `softwareSystem` per\n * service (the `metadata { service '<id>' }` binding is what every loam join\n * reads — element ids are cosmetic), `#external` systems for the externals,\n * and PLAIN edges. No operationIds: those arrive with contracts, and a\n * templated guess would be the extractor this command exists not to be. No\n * relationship kinds on the edges either, for the same reason — but the kinds\n * themselves are DECLARED, as the scaffold's stub declares them, so whatever\n * draws the next edge can name one.\n */\nimport type { FleetSeed } from \"./fleet-file.js\";\n\n/** Which kind of declared name an element id is being built for. */\ntype ElementKind = \"svc\" | \"ext\";\n\n/**\n * A LikeC4 identifier for a declared name, mechanically: `.` and `-` become\n * `_`, and the result takes its kind's prefix — `svc_` for a service, `ext_`\n * for an external.\n *\n * The prefix is not decoration, and not only about the digit-initial case\n * (LikeC4 identifiers are letter/underscore-initial). LikeC4's KEYWORDS are\n * bare lowercase words — `notes`, `tag`, `view`, `metadata`, `style`, `link`,\n * `import`, `description`, `size`, `line`, `this` and some thirty more — and a\n * service legally named `notes` emitted as a bare `notes = softwareSystem`\n * does not parse. The self-check would catch that, but only to refuse a fleet\n * whose fault it is not, under `internal`, with no fix to offer. No keyword\n * contains `_`, so a prefixed identifier is PROVABLY not one — at this LikeC4\n * version and at every later one. A reserved-word list here would be the same\n * bug again, rotting the first time the language grew a word.\n *\n * The fold is not injective — `a-b` and `a.b` both give `a_b` — so residual\n * collisions take a deterministic numeric suffix in sorted processing order.\n * Aesthetics do not matter here: the binding metadata, not the element id, is\n * the element's identity to loam.\n */\nfunction foldName(name: string, kind: ElementKind): string {\n return `${kind}_${name.replace(/[.-]/g, \"_\")}`;\n}\n\n/**\n * The element id loam SUGGESTS for a service no map draws yet — this seed's\n * own convention, exported so it is the only one. The adopt brief and the\n * `loam new` delta used to camel-case the directory name instead, so a seeded\n * fleet received three spellings of one box (`svc_payment_service` on the\n * map, `paymentService` in every brief and scaffold), and a delta that reused\n * the scaffold's guess never lined up with the map it was told to match. The\n * id stays cosmetic — `metadata { service '<id>' }` is the join — but a\n * suggestion gets pasted, so it has to be one spelling, and the keyword-proof\n * one above is the one already on disk in every seeded repo.\n */\nexport function serviceElementId(service: string): string {\n return foldName(service, \"svc\");\n}\n\n/** Element ids for every declared name (services ∪ externals), collision-suffixed deterministically. */\nfunction elementIds(seed: FleetSeed): Map<string, string> {\n const ids = new Map<string, string>();\n const used = new Set<string>();\n const named: Array<readonly [string, ElementKind]> = [\n ...[...seed.services]\n .map((s) => s.id as string)\n .sort()\n .map((n) => [n, \"svc\"] as const),\n ...[...seed.externals].sort().map((n) => [n, \"ext\"] as const),\n ];\n for (const [name, kind] of named) {\n let candidate = foldName(name, kind);\n for (let n = 2; used.has(candidate); n += 1) candidate = `${foldName(name, kind)}_${n}`;\n used.add(candidate);\n ids.set(name, candidate);\n }\n return ids;\n}\n\n/** Render the landscape body (unstamped — `sealLandscape` adds line 1). */\nexport function renderLandscape(seed: FleetSeed): string {\n const ids = elementIds(seed);\n // Every name reaching `el` was declared (calls are validated against\n // services ∪ externals before rendering), so the fallback is unreachable —\n // it exists so a future caller cannot get a silent `undefined` into the DSL.\n const el = (name: string): string => ids.get(name) ?? foldName(name, \"svc\");\n\n const lines: string[] = [\n \"// Generated from fleet.yaml by `loam seed` — edit fleet.yaml and re-run to\",\n \"// regenerate. Hand edits make this file yours: the stamp on line 1 is how\",\n \"// seed tells, and it will then refuse to touch the file again.\",\n \"//\",\n \"// As contracts arrive, give each call the operationId it uses, so\",\n \"// requirements, C4 and OpenAPI can be cross-checked:\",\n \"//\",\n \"// svc_checkout -> svc_payment_service 'Calls authorizePayment' {\",\n \"// metadata { op 'authorizePayment' }\",\n \"// }\",\n \"\",\n \"specification {\",\n \" element person\",\n \" element softwareSystem\",\n \" element container\",\n \" element database\",\n \"\",\n \" // #platform keeps ubiquitous infrastructure out of the fleet view;\",\n \" // #external marks somebody else's system, so validate does not ask for a\",\n \" // services/ directory nobody owes. A tag must be declared before it is\",\n \" // used, so both are declared here whether or not this fleet uses them.\",\n \" tag platform\",\n \" tag external\",\n // The relationship kinds are DUPLICATED from core/scaffold/landscape.ts's\n // LANDSCAPE_STUB for the views block's reason below, and test/seed.test.ts\n // compares the two byte for byte the same way. Declared, never used: the\n // calls below stay plain edges, because fleet.yaml says who calls whom and\n // has no spelling for what an edge carries — choosing a kind here would be\n // the guess this templater exists not to make. Declaring them is what lets\n // the adoptions and feature deltas that follow draw kinded edges into this\n // map, and lets an archive that copies one in still parse.\n \"\",\n \" // One kind per kind of traffic, each with one direction. The style is the\",\n \" // picture: amber for messages, green for data at rest, gray and dashed for\",\n \" // a connection nobody has given a direction yet. Never tag a kind: LikeC4\",\n \" // copies a kind's tags onto every edge of it.\",\n \" relationship publishes {\",\n \" color amber\",\n \" line solid\",\n \" head normal\",\n \" }\",\n \" relationship delivers {\",\n \" color amber\",\n \" line solid\",\n \" head normal\",\n \" }\",\n \" relationship reads {\",\n \" color green\",\n \" head none\",\n \" tail normal\",\n \" }\",\n \" relationship writes {\",\n \" color green\",\n \" head normal\",\n \" }\",\n \" relationship readsWrites {\",\n \" color green\",\n \" head normal\",\n \" tail normal\",\n \" }\",\n \" relationship calls {\",\n \" color secondary\",\n \" line solid\",\n \" head normal\",\n \" }\",\n \" relationship connects {\",\n \" color gray\",\n \" line dashed\",\n \" head none\",\n \" // A marker at the source: an edge with no arrowhead at either end is laid\",\n \" // out unconstrained, and enough of them flatten a view into one band.\",\n \" tail odot\",\n \" }\",\n \"}\",\n \"\",\n \"model {\",\n ];\n\n const services = [...seed.services].sort((a, b) => (a.id < b.id ? -1 : 1));\n for (const s of services) {\n lines.push(` ${el(s.id)} = softwareSystem '${s.id}' {`);\n lines.push(` metadata { service '${s.id}' }`);\n lines.push(\" }\");\n }\n const externals = [...seed.externals].sort();\n for (const x of externals) {\n lines.push(` ${el(x)} = softwareSystem '${x}' {`);\n lines.push(\" #external\");\n lines.push(\" }\");\n }\n const calls = [...seed.calls].sort((a, b) =>\n a.from === b.from ? (a.to < b.to ? -1 : 1) : a.from < b.from ? -1 : 1,\n );\n if (calls.length > 0) lines.push(\"\");\n for (const c of calls) lines.push(` ${el(c.from)} -> ${el(c.to)}`);\n lines.push(\"}\");\n\n // The views block below is DUPLICATED from core/docs.ts's LANDSCAPE_STUB\n // (two copies — extract on a third, per the house rule). The tripwire in\n // test/seed.test.ts compares the two blocks BYTE FOR BYTE, comments and\n // wrapping included, so whichever copy drifts fails the suite — a\n // view-names-only compare let a corrected predicate land in one copy and\n // leave every seeded fleet map rendering the old one.\n // The emitted comment speaks to the docs repo's reader, not to loam's:\n // internal paths mean nothing where this file lands.\n lines.push(\n \"\",\n \"// The views are for LikeC4's own tooling — loam reads the model and never\",\n \"// a view. Render with `npx likec4 start` from the docs repo root. Tag\",\n \"// ubiquitous infrastructure #platform to keep the fleet view readable.\",\n \"views {\",\n \" view fleet {\",\n \" include *\",\n \" exclude element.tag = #platform\",\n \" }\",\n \" // The obvious spelling — `include element.tag = #platform` — draws the\",\n \" // platform boxes with NO edges and no consumers. The predicate that works is\",\n \" // the relationship form below.\",\n \" view platform {\",\n \" include * -> element.tag = #platform\",\n \" }\",\n \"}\",\n );\n return lines.join(\"\\n\") + \"\\n\";\n}\n"]}
|