@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,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The verification record read back, and refused when it cannot be trusted.
|
|
3
|
+
*
|
|
4
|
+
* The record is several files now: the one-file `verification.yaml` (the
|
|
5
|
+
* all-at-once form, and every federated record written before the directory
|
|
6
|
+
* existed) and one `verification/<svc>.yaml` per attesting service. Each file
|
|
7
|
+
* is graded on its own here, and `./union.ts` adds the graded files up
|
|
8
|
+
* into the one `Verification` every reader takes — so no reader learns the
|
|
9
|
+
* layout, and a record that is half one form and half the other reads as one
|
|
10
|
+
* record.
|
|
11
|
+
*
|
|
12
|
+
* Reading is deliberately not the inverse of rendering. A file loam wrote is
|
|
13
|
+
* re-graded on the way in — git conflict markers, unreadable YAML, a shape that
|
|
14
|
+
* is not a verification, a `summary` that disagrees with the claims below it —
|
|
15
|
+
* because the record is data meant to survive without loam, and anything may
|
|
16
|
+
* have edited it since. The render lives with the writer it feeds
|
|
17
|
+
* (`../store/render.ts` produces exactly the bytes `../store/commit.ts` swaps into
|
|
18
|
+
* place; races and kills mid-write live there too), and the lock that
|
|
19
|
+
* serialises writers is taken by `commands/verify/verify.ts` before this
|
|
20
|
+
* module's authoritative read. Nothing here touches the filesystem except to
|
|
21
|
+
* read.
|
|
22
|
+
*
|
|
23
|
+
* A package of its own (`core/verify/read/`, moved from `core/verify/file.ts`)
|
|
24
|
+
* because the reader now leans on two modules that lean back on
|
|
25
|
+
* `../record.ts`: kept in `core/verify` beside them it would have been a
|
|
26
|
+
* package cycle that `import/no-cycle` cannot see.
|
|
27
|
+
*/
|
|
28
|
+
import { lstat, readdir, readFile } from "node:fs/promises";
|
|
29
|
+
import { join } from "node:path";
|
|
30
|
+
import { parse } from "yaml";
|
|
31
|
+
import { conflictMarkerLines } from "../../conflict-markers.js";
|
|
32
|
+
import { inOrder } from "../../kernel/concurrency.js";
|
|
33
|
+
import { verificationDir, verificationPath } from "../location.js";
|
|
34
|
+
import {} from "../record.js";
|
|
35
|
+
import { asVerification, summaryDisagreement } from "./shape.js";
|
|
36
|
+
import { assembleRecord } from "./union.js";
|
|
37
|
+
/**
|
|
38
|
+
* `live` is the digest of the checklist the caller knows is being asked — see
|
|
39
|
+
* `assembleRecord` for why the union needs it. A caller that only learns it
|
|
40
|
+
* after the read (it derives the checklist only when there IS a record) adds
|
|
41
|
+
* the same files up again with {@link answering} instead of reading twice.
|
|
42
|
+
*/
|
|
43
|
+
export async function readVerificationState(featureDir, live) {
|
|
44
|
+
const legacy = await readRecordFile(verificationPath(featureDir), null);
|
|
45
|
+
if (legacy.state === "unreadable")
|
|
46
|
+
return legacy;
|
|
47
|
+
const listed = await serviceFiles(featureDir);
|
|
48
|
+
if (!Array.isArray(listed))
|
|
49
|
+
return listed;
|
|
50
|
+
// In parallel — the files are independent, and a fleet-sized feature has
|
|
51
|
+
// one per service — but graded in the listing's sorted order, so the file a
|
|
52
|
+
// refusal names is the first unreadable one by name on every machine, never
|
|
53
|
+
// whichever read happened to fail first.
|
|
54
|
+
const reads = await inOrder(listed, ({ service, path }) => readRecordFile(path, service));
|
|
55
|
+
const services = new Map();
|
|
56
|
+
for (const [i, read] of reads.entries()) {
|
|
57
|
+
if (read.state === "unreadable")
|
|
58
|
+
return read;
|
|
59
|
+
// Listed and then gone is a file a concurrent writer removed: absent.
|
|
60
|
+
if (read.state === "ok")
|
|
61
|
+
services.set(listed[i].service, read.file);
|
|
62
|
+
}
|
|
63
|
+
const files = { legacy: legacy.state === "ok" ? legacy.file : null, services };
|
|
64
|
+
const verification = answering(files, live);
|
|
65
|
+
if (verification === null)
|
|
66
|
+
return { state: "absent" };
|
|
67
|
+
return { state: "ok", verification, files };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The record's files added up for the checklist `live` asks — what
|
|
71
|
+
* {@link readVerificationState} answers, and what it would have answered for a
|
|
72
|
+
* caller that only learned `live` after the read. Pure: the files are the ones
|
|
73
|
+
* that read already graded. Null only when there are no files at all.
|
|
74
|
+
*/
|
|
75
|
+
export function answering(files, live) {
|
|
76
|
+
return assembleRecord(files.legacy?.verification ?? null, [...files.services.values()].map((file) => file.verification), live);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The per-service files, sorted by service id so the union is the same on
|
|
80
|
+
* every machine. Dotfiles are skipped — staging parks its temp files beside
|
|
81
|
+
* the target as `.<name>.loam-….tmp`, and a reader racing a writer must not
|
|
82
|
+
* grade one — and so is anything that is not `*.yaml`: the directory is the
|
|
83
|
+
* list of attestations, and a README beside them is not one.
|
|
84
|
+
*/
|
|
85
|
+
async function serviceFiles(featureDir) {
|
|
86
|
+
const dir = verificationDir(featureDir);
|
|
87
|
+
let entries;
|
|
88
|
+
try {
|
|
89
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
if (err.code === "ENOENT")
|
|
93
|
+
return [];
|
|
94
|
+
return {
|
|
95
|
+
state: "unreadable",
|
|
96
|
+
path: dir,
|
|
97
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
98
|
+
advice: "It has to be a directory holding one <svc>.yaml per attesting service; move whatever is there aside, then record again.",
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return entries
|
|
102
|
+
.filter((e) => !e.name.startsWith(".") && e.name.endsWith(".yaml") && !e.isDirectory())
|
|
103
|
+
.map((e) => ({ service: e.name.slice(0, -".yaml".length), path: join(dir, e.name) }))
|
|
104
|
+
.sort((a, b) => (a.service < b.service ? -1 : a.service > b.service ? 1 : 0));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The recipe for a file loam could not use. See {@link VerificationRead} for
|
|
108
|
+
* why the two kinds of file get different ones. Neither tells anybody to edit
|
|
109
|
+
* the counts — a summary made to agree by hand still says nothing about what
|
|
110
|
+
* was checked — and both end in a record that can actually run: the old
|
|
111
|
+
* one-size "record the answers again" sent a miscounted record straight back
|
|
112
|
+
* into this refusal, because `--record` will not write over a file it could
|
|
113
|
+
* not read.
|
|
114
|
+
*/
|
|
115
|
+
function adviceFor(service) {
|
|
116
|
+
if (service !== null) {
|
|
117
|
+
return (`It is ${service}'s attestation file and holds no other service's word: repair it by hand, or delete it and ` +
|
|
118
|
+
`record ${service} again from that service's own repository (\`--service ${service} --record\`). ` +
|
|
119
|
+
"After a merge, both sides of a conflict in it are that one service's attestations.");
|
|
120
|
+
}
|
|
121
|
+
return ("It may hold other services' attestations, so do not delete it: repair it by hand, or restore it from version " +
|
|
122
|
+
"control — after a merge, take the copy on the branch you merged into — and then record again. A `--service` " +
|
|
123
|
+
"record goes to verification/<svc>.yaml beside it and leaves this file as it is.");
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* One file of the record, graded. `service` is the file's own service for a
|
|
127
|
+
* `verification/<svc>.yaml` — the file NAME is the claim about whose word it
|
|
128
|
+
* holds, so the content has to agree with it — and null for the one-file
|
|
129
|
+
* record.
|
|
130
|
+
*/
|
|
131
|
+
async function readRecordFile(path, service) {
|
|
132
|
+
// The code rides in an object, as a `code:` property, rather than as a
|
|
133
|
+
// parameter: a parameter named `code` makes this helper an emitter to the
|
|
134
|
+
// stable-code collector, which then cannot read the slot at the call sites
|
|
135
|
+
// that leave it out.
|
|
136
|
+
const unreadable = (reason, coded = {}) => ({
|
|
137
|
+
state: "unreadable",
|
|
138
|
+
path,
|
|
139
|
+
reason,
|
|
140
|
+
advice: adviceFor(service),
|
|
141
|
+
...coded,
|
|
142
|
+
});
|
|
143
|
+
let raw;
|
|
144
|
+
try {
|
|
145
|
+
raw = await readFile(path);
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
if (err.code === "ENOENT") {
|
|
149
|
+
// ENOENT is "absent" — except from a symlink whose target is gone, which
|
|
150
|
+
// `existsSync` used to fold into the same answer. That mislabel was a
|
|
151
|
+
// trap: the read said "no record" while the commit's exclusive link(2)
|
|
152
|
+
// saw the link itself and refused EEXIST, so every `--record` answered
|
|
153
|
+
// `record-raced` with advice (re-run) that could never work. The lstat
|
|
154
|
+
// must CONFIRM the link before the message asserts one: a real file can
|
|
155
|
+
// also appear between the two calls, and telling its owner to "remove
|
|
156
|
+
// the link" would delete a valid record.
|
|
157
|
+
let entry;
|
|
158
|
+
try {
|
|
159
|
+
entry = await lstat(path);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return { state: "absent" };
|
|
163
|
+
}
|
|
164
|
+
if (entry.isSymbolicLink()) {
|
|
165
|
+
return unreadable("it is a symlink whose target does not exist — replace the link with a real record, or remove it");
|
|
166
|
+
}
|
|
167
|
+
return unreadable("it appeared while being read — another writer is landing; re-run");
|
|
168
|
+
}
|
|
169
|
+
return unreadable(err instanceof Error ? err.message : String(err));
|
|
170
|
+
}
|
|
171
|
+
const text = raw.toString("utf8");
|
|
172
|
+
// Before the parser, because the parser's answer to a merge is the wrong
|
|
173
|
+
// one: YAML either fails on a line that is not the problem or — where the
|
|
174
|
+
// markers land inside a block scalar — reads both sides as one plausible
|
|
175
|
+
// document. Neither says "a merge left this", which is the one sentence that
|
|
176
|
+
// leads to the right fix.
|
|
177
|
+
const markers = conflictMarkerLines(text);
|
|
178
|
+
if (markers.length > 0) {
|
|
179
|
+
return unreadable(`it still contains git conflict markers (line${markers.length === 1 ? "" : "s"} ${markers.join(", ")}) — ` +
|
|
180
|
+
"a merge left both sides in it, so nothing it says is anyone's record");
|
|
181
|
+
}
|
|
182
|
+
let doc;
|
|
183
|
+
try {
|
|
184
|
+
doc = parse(text);
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
return unreadable(yamlReason(err));
|
|
188
|
+
}
|
|
189
|
+
const verification = asVerification(doc);
|
|
190
|
+
if (verification === null) {
|
|
191
|
+
return unreadable("it parses as YAML but does not have a verification record's shape (feature, recorded, checklist, summary and claims)");
|
|
192
|
+
}
|
|
193
|
+
const foreign = service === null ? null : notOnlyFrom(verification, service);
|
|
194
|
+
if (foreign !== null)
|
|
195
|
+
return unreadable(foreign);
|
|
196
|
+
const miscount = summaryDisagreement(verification);
|
|
197
|
+
// Spelled twice on purpose: the field is what a caller branches on, the
|
|
198
|
+
// prose is what a terminal prints and what somebody greps for.
|
|
199
|
+
if (miscount !== null)
|
|
200
|
+
return unreadable(`verify.record-miscounted: ${miscount}`, { code: "verify.record-miscounted" });
|
|
201
|
+
return { state: "ok", file: { path, verification, raw } };
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* A per-service file must be exactly what its name says: a federated record
|
|
205
|
+
* carrying that one service's attestation and that service's answers. loam
|
|
206
|
+
* never writes anything else there, and the advice for a damaged file — delete
|
|
207
|
+
* it and record that service again — is only safe while it is true.
|
|
208
|
+
*/
|
|
209
|
+
function notOnlyFrom(v, service) {
|
|
210
|
+
const attested = (v.attestations ?? []).map((a) => a.service);
|
|
211
|
+
if (v.schema !== 2 || attested.length !== 1 || attested[0] !== service) {
|
|
212
|
+
return `it is named for ${service} but does not hold exactly ${service}'s attestation (it holds ${attested.length === 0 ? "none" : attested.join(", ")})`;
|
|
213
|
+
}
|
|
214
|
+
const strays = v.claims.filter((c) => c.subject !== service).map((c) => c.id);
|
|
215
|
+
return strays.length === 0 ? null : `it is named for ${service} but answers claims filed under another service: ${strays.join(", ")}`;
|
|
216
|
+
}
|
|
217
|
+
/** A YAML failure with its line, when the parser located one: that line is the fix. */
|
|
218
|
+
function yamlReason(err) {
|
|
219
|
+
const message = err instanceof Error ? err.message.split("\n")[0].trim() : String(err);
|
|
220
|
+
const pos = err?.linePos;
|
|
221
|
+
const line = Array.isArray(pos) ? pos[0]?.line : undefined;
|
|
222
|
+
return line === undefined ? `YAML error: ${message}` : `YAML error at line ${line}: ${message}`;
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../../../src/core/verify/read/file.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The verification record read back, and refused when it cannot be trusted.\n *\n * The record is several files now: the one-file `verification.yaml` (the\n * all-at-once form, and every federated record written before the directory\n * existed) and one `verification/<svc>.yaml` per attesting service. Each file\n * is graded on its own here, and `./union.ts` adds the graded files up\n * into the one `Verification` every reader takes — so no reader learns the\n * layout, and a record that is half one form and half the other reads as one\n * record.\n *\n * Reading is deliberately not the inverse of rendering. A file loam wrote is\n * re-graded on the way in — git conflict markers, unreadable YAML, a shape that\n * is not a verification, a `summary` that disagrees with the claims below it —\n * because the record is data meant to survive without loam, and anything may\n * have edited it since. The render lives with the writer it feeds\n * (`../store/render.ts` produces exactly the bytes `../store/commit.ts` swaps into\n * place; races and kills mid-write live there too), and the lock that\n * serialises writers is taken by `commands/verify/verify.ts` before this\n * module's authoritative read. Nothing here touches the filesystem except to\n * read.\n *\n * A package of its own (`core/verify/read/`, moved from `core/verify/file.ts`)\n * because the reader now leans on two modules that lean back on\n * `../record.ts`: kept in `core/verify` beside them it would have been a\n * package cycle that `import/no-cycle` cannot see.\n */\nimport { lstat, readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { parse } from \"yaml\";\nimport { conflictMarkerLines } from \"../../conflict-markers.js\";\nimport { inOrder } from \"../../kernel/concurrency.js\";\nimport { verificationDir, verificationPath } from \"../location.js\";\nimport { type Verification } from \"../record.js\";\nimport { asVerification, summaryDisagreement } from \"./shape.js\";\nimport { assembleRecord } from \"./union.js\";\n\n/**\n * What is on disk beside a feature: nothing, something unreadable, or a record.\n *\n * The three are kept apart because they call for opposite handling. \"Absent\" is\n * a feature nobody has verified — verify starts a fresh record over it without\n * a second thought. \"Unreadable\" is a file that IS somebody's record: it parses\n * as garbage, or its shape is not a record's, and every answer and attestation\n * it holds is unaccounted for. Collapsing the two (which this function used to\n * do, returning null for both) meant a hand-edited or half-written\n * verification.yaml read as \"not verified\" and the next `--record` overwrote a\n * whole fleet's attestations without a word.\n */\nexport type VerificationRead =\n | { state: \"absent\" }\n /**\n * `path` is the ONE file that failed — with several files making up the\n * record, \"the record is unreadable\" names nothing a person can open.\n * `reason` names the YAML line when the parser gave one, or the conflict\n * markers' lines. `advice` is the recipe, and it differs by file on purpose:\n * a `verification/<svc>.yaml` holds one service's word, so deleting it and\n * recording that service again loses nobody else's evidence, while the\n * one-file record may hold a whole fleet's attestations and must never be\n * the file a recipe tells somebody to delete. `code` is set for the one\n * unreadable that parses perfectly: a file whose `summary` contradicts its\n * own `claims[]`. That lives here rather than in a state of its own so every\n * reader which already refuses an unreadable record refuses this one too,\n * without a line of new code.\n */\n | { state: \"unreadable\"; path: string; reason: string; advice: string; code?: \"verify.record-miscounted\" }\n /**\n * `verification` is the union the readers use; `files` is what it was added\n * up from, each with the exact bytes it was parsed from. A writer merges over\n * the PARSE, but commits over the BYTES: `commitVerification` compares each\n * file it replaces or deletes against these immediately before its swap, so\n * an edit that landed between this read and that write — an editor, a writer\n * that ignored the docs lock — is refused and preserved instead of silently\n * buried.\n */\n | { state: \"ok\"; verification: Verification; files: RecordFiles };\n\n/** One graded file of the record, with the bytes it was graded from. */\nexport interface RecordFile {\n path: string;\n verification: Verification;\n raw: Buffer;\n}\n\n/** The files a record was added up from: the one-file form, and the per-service files by service id. */\nexport interface RecordFiles {\n legacy: RecordFile | null;\n services: ReadonlyMap<string, RecordFile>;\n}\n\ntype Unreadable = Extract<VerificationRead, { state: \"unreadable\" }>;\ntype FileRead = { state: \"absent\" } | Unreadable | { state: \"ok\"; file: RecordFile };\n\n/**\n * `live` is the digest of the checklist the caller knows is being asked — see\n * `assembleRecord` for why the union needs it. A caller that only learns it\n * after the read (it derives the checklist only when there IS a record) adds\n * the same files up again with {@link answering} instead of reading twice.\n */\nexport async function readVerificationState(featureDir: string, live?: string): Promise<VerificationRead> {\n const legacy = await readRecordFile(verificationPath(featureDir), null);\n if (legacy.state === \"unreadable\") return legacy;\n const listed = await serviceFiles(featureDir);\n if (!Array.isArray(listed)) return listed;\n // In parallel — the files are independent, and a fleet-sized feature has\n // one per service — but graded in the listing's sorted order, so the file a\n // refusal names is the first unreadable one by name on every machine, never\n // whichever read happened to fail first.\n const reads = await inOrder(listed, ({ service, path }) => readRecordFile(path, service));\n const services = new Map<string, RecordFile>();\n for (const [i, read] of reads.entries()) {\n if (read.state === \"unreadable\") return read;\n // Listed and then gone is a file a concurrent writer removed: absent.\n if (read.state === \"ok\") services.set(listed[i]!.service, read.file);\n }\n const files: RecordFiles = { legacy: legacy.state === \"ok\" ? legacy.file : null, services };\n const verification = answering(files, live);\n if (verification === null) return { state: \"absent\" };\n return { state: \"ok\", verification, files };\n}\n\n/**\n * The record's files added up for the checklist `live` asks — what\n * {@link readVerificationState} answers, and what it would have answered for a\n * caller that only learned `live` after the read. Pure: the files are the ones\n * that read already graded. Null only when there are no files at all.\n */\nexport function answering(files: RecordFiles, live: string | undefined): Verification | null {\n return assembleRecord(\n files.legacy?.verification ?? null,\n [...files.services.values()].map((file) => file.verification),\n live,\n );\n}\n\n/**\n * The per-service files, sorted by service id so the union is the same on\n * every machine. Dotfiles are skipped — staging parks its temp files beside\n * the target as `.<name>.loam-….tmp`, and a reader racing a writer must not\n * grade one — and so is anything that is not `*.yaml`: the directory is the\n * list of attestations, and a README beside them is not one.\n */\nasync function serviceFiles(featureDir: string): Promise<Array<{ service: string; path: string }> | Unreadable> {\n const dir = verificationDir(featureDir);\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n return {\n state: \"unreadable\",\n path: dir,\n reason: err instanceof Error ? err.message : String(err),\n advice: \"It has to be a directory holding one <svc>.yaml per attesting service; move whatever is there aside, then record again.\",\n };\n }\n return entries\n .filter((e) => !e.name.startsWith(\".\") && e.name.endsWith(\".yaml\") && !e.isDirectory())\n .map((e) => ({ service: e.name.slice(0, -\".yaml\".length), path: join(dir, e.name) }))\n .sort((a, b) => (a.service < b.service ? -1 : a.service > b.service ? 1 : 0));\n}\n\n/**\n * The recipe for a file loam could not use. See {@link VerificationRead} for\n * why the two kinds of file get different ones. Neither tells anybody to edit\n * the counts — a summary made to agree by hand still says nothing about what\n * was checked — and both end in a record that can actually run: the old\n * one-size \"record the answers again\" sent a miscounted record straight back\n * into this refusal, because `--record` will not write over a file it could\n * not read.\n */\nfunction adviceFor(service: string | null): string {\n if (service !== null) {\n return (\n `It is ${service}'s attestation file and holds no other service's word: repair it by hand, or delete it and ` +\n `record ${service} again from that service's own repository (\\`--service ${service} --record\\`). ` +\n \"After a merge, both sides of a conflict in it are that one service's attestations.\"\n );\n }\n return (\n \"It may hold other services' attestations, so do not delete it: repair it by hand, or restore it from version \" +\n \"control — after a merge, take the copy on the branch you merged into — and then record again. A `--service` \" +\n \"record goes to verification/<svc>.yaml beside it and leaves this file as it is.\"\n );\n}\n\n/**\n * One file of the record, graded. `service` is the file's own service for a\n * `verification/<svc>.yaml` — the file NAME is the claim about whose word it\n * holds, so the content has to agree with it — and null for the one-file\n * record.\n */\nasync function readRecordFile(path: string, service: string | null): Promise<FileRead> {\n // The code rides in an object, as a `code:` property, rather than as a\n // parameter: a parameter named `code` makes this helper an emitter to the\n // stable-code collector, which then cannot read the slot at the call sites\n // that leave it out.\n const unreadable = (reason: string, coded: { code?: \"verify.record-miscounted\" } = {}): Unreadable => ({\n state: \"unreadable\",\n path,\n reason,\n advice: adviceFor(service),\n ...coded,\n });\n let raw: Buffer;\n try {\n raw = await readFile(path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n // ENOENT is \"absent\" — except from a symlink whose target is gone, which\n // `existsSync` used to fold into the same answer. That mislabel was a\n // trap: the read said \"no record\" while the commit's exclusive link(2)\n // saw the link itself and refused EEXIST, so every `--record` answered\n // `record-raced` with advice (re-run) that could never work. The lstat\n // must CONFIRM the link before the message asserts one: a real file can\n // also appear between the two calls, and telling its owner to \"remove\n // the link\" would delete a valid record.\n let entry;\n try {\n entry = await lstat(path);\n } catch {\n return { state: \"absent\" };\n }\n if (entry.isSymbolicLink()) {\n return unreadable(\"it is a symlink whose target does not exist — replace the link with a real record, or remove it\");\n }\n return unreadable(\"it appeared while being read — another writer is landing; re-run\");\n }\n return unreadable(err instanceof Error ? err.message : String(err));\n }\n const text = raw.toString(\"utf8\");\n // Before the parser, because the parser's answer to a merge is the wrong\n // one: YAML either fails on a line that is not the problem or — where the\n // markers land inside a block scalar — reads both sides as one plausible\n // document. Neither says \"a merge left this\", which is the one sentence that\n // leads to the right fix.\n const markers = conflictMarkerLines(text);\n if (markers.length > 0) {\n return unreadable(\n `it still contains git conflict markers (line${markers.length === 1 ? \"\" : \"s\"} ${markers.join(\", \")}) — ` +\n \"a merge left both sides in it, so nothing it says is anyone's record\",\n );\n }\n let doc: unknown;\n try {\n doc = parse(text);\n } catch (err) {\n return unreadable(yamlReason(err));\n }\n const verification = asVerification(doc);\n if (verification === null) {\n return unreadable(\n \"it parses as YAML but does not have a verification record's shape (feature, recorded, checklist, summary and claims)\",\n );\n }\n const foreign = service === null ? null : notOnlyFrom(verification, service);\n if (foreign !== null) return unreadable(foreign);\n const miscount = summaryDisagreement(verification);\n // Spelled twice on purpose: the field is what a caller branches on, the\n // prose is what a terminal prints and what somebody greps for.\n if (miscount !== null) return unreadable(`verify.record-miscounted: ${miscount}`, { code: \"verify.record-miscounted\" });\n return { state: \"ok\", file: { path, verification, raw } };\n}\n\n/**\n * A per-service file must be exactly what its name says: a federated record\n * carrying that one service's attestation and that service's answers. loam\n * never writes anything else there, and the advice for a damaged file — delete\n * it and record that service again — is only safe while it is true.\n */\nfunction notOnlyFrom(v: Verification, service: string): string | null {\n const attested = (v.attestations ?? []).map((a) => a.service);\n if (v.schema !== 2 || attested.length !== 1 || attested[0] !== service) {\n return `it is named for ${service} but does not hold exactly ${service}'s attestation (it holds ${attested.length === 0 ? \"none\" : attested.join(\", \")})`;\n }\n const strays = v.claims.filter((c) => c.subject !== service).map((c) => c.id);\n return strays.length === 0 ? null : `it is named for ${service} but answers claims filed under another service: ${strays.join(\", \")}`;\n}\n\n/** A YAML failure with its line, when the parser located one: that line is the fix. */\nfunction yamlReason(err: unknown): string {\n const message = err instanceof Error ? err.message.split(\"\\n\")[0]!.trim() : String(err);\n const pos = (err as { linePos?: Array<{ line: number; col: number }> } | null)?.linePos;\n const line = Array.isArray(pos) ? pos[0]?.line : undefined;\n return line === undefined ? `YAML error: ${message}` : `YAML error at line ${line}: ${message}`;\n}\n"]}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a file must hold to be a verification record — the shape check and the
|
|
3
|
+
* self-consistency check every file of the record passes on the way in.
|
|
4
|
+
*
|
|
5
|
+
* Moved out of `./file.ts` when the record became several files: that module
|
|
6
|
+
* now owns WHICH files make up a record and how a damaged one is reported,
|
|
7
|
+
* while this one owns what any single file has to say to be read at all. The
|
|
8
|
+
* two are phases — find the files, then grade each — and the grading never
|
|
9
|
+
* needed to know where its bytes came from.
|
|
10
|
+
*/
|
|
11
|
+
import { isRecord } from "../../kernel/records.js";
|
|
12
|
+
import { VERDICTS } from "../answers.js";
|
|
13
|
+
import { isEvidencePinList } from "../pins/pin.js";
|
|
14
|
+
import { tallyAnswers } from "../record.js";
|
|
15
|
+
/**
|
|
16
|
+
* Does the record's `summary` add up from its own `claims[]`?
|
|
17
|
+
*
|
|
18
|
+
* The summary is what wrote the file; the claims are what it answered. Nothing
|
|
19
|
+
* ever compared them, so every reader that took the shortcut — verify's frozen
|
|
20
|
+
* verdict for a shipped feature, list's column, status's block — reported a
|
|
21
|
+
* record full of unconfirmed claims as fully confirmed. Believing either side
|
|
22
|
+
* over the other would be a guess about which half was tampered with, so a
|
|
23
|
+
* record that contradicts itself is refused like any other file loam cannot
|
|
24
|
+
* use. The code is in the reason: it travels wherever the reason is printed.
|
|
25
|
+
*/
|
|
26
|
+
export function summaryDisagreement(v) {
|
|
27
|
+
const t = tallyAnswers(v.claims);
|
|
28
|
+
const unanswered = v.summary.unanswered ?? 0;
|
|
29
|
+
const says = [];
|
|
30
|
+
// Before the arithmetic, because a negative count defeats it: `unanswered:
|
|
31
|
+
// -2` is never something loam wrote, and it is the one value that lets a
|
|
32
|
+
// hand-edited summary balance its own books — every comparison below then
|
|
33
|
+
// agrees, and a record contradicting its claims reported `verified`.
|
|
34
|
+
for (const [field, value] of Object.entries(v.summary)) {
|
|
35
|
+
if (typeof value === "number" && value < 0)
|
|
36
|
+
says.push(`carries a negative count (${field}: ${value})`);
|
|
37
|
+
}
|
|
38
|
+
if (v.summary.confirmed !== t.confirmed) {
|
|
39
|
+
says.push(`says ${v.summary.confirmed} confirmed where claims[] holds ${t.confirmed}`);
|
|
40
|
+
}
|
|
41
|
+
if (v.summary.unconfirmed !== t.unconfirmed) {
|
|
42
|
+
says.push(`says ${v.summary.unconfirmed} unconfirmed where claims[] holds ${t.unconfirmed}`);
|
|
43
|
+
}
|
|
44
|
+
if (v.summary.claims !== v.claims.length + unanswered) {
|
|
45
|
+
says.push(`says ${v.summary.claims} claim(s)${unanswered === 0 ? "" : ` including ${unanswered} unanswered`} where claims[] holds ${v.claims.length}`);
|
|
46
|
+
}
|
|
47
|
+
return says.length === 0
|
|
48
|
+
? null
|
|
49
|
+
: `its summary ${says.join(", ")} — the record contradicts itself, so neither half can be believed`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The shape check `readVerificationState` (`./file.ts`) stands behind:
|
|
53
|
+
* everything its readers
|
|
54
|
+
* dereference (verify's report/frozen views, list's verification column) must
|
|
55
|
+
* be present and typed, or the cast would manufacture a Verification that
|
|
56
|
+
* crashes them. Deliberately no tighter than the readers need: extra keys pass
|
|
57
|
+
* (a human may annotate), `answered_by` stays optional (absent in records that
|
|
58
|
+
* predate `--results`) and is any string, like `kind` — the readers only ever
|
|
59
|
+
* compare them, and a record written by a newer loam must not read as absent.
|
|
60
|
+
*/
|
|
61
|
+
export function asVerification(doc) {
|
|
62
|
+
if (!isRecord(doc))
|
|
63
|
+
return null;
|
|
64
|
+
const summary = doc["summary"];
|
|
65
|
+
if (typeof doc["feature"] !== "string" ||
|
|
66
|
+
typeof doc["recorded"] !== "string" ||
|
|
67
|
+
typeof doc["checklist"] !== "string" ||
|
|
68
|
+
!isRecord(summary) ||
|
|
69
|
+
!isCount(summary["claims"]) ||
|
|
70
|
+
!isCount(summary["confirmed"]) ||
|
|
71
|
+
!isCount(summary["unconfirmed"]) ||
|
|
72
|
+
(summary["unanswered"] !== undefined && !isCount(summary["unanswered"])) ||
|
|
73
|
+
!Array.isArray(doc["claims"])) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
for (const c of doc["claims"]) {
|
|
77
|
+
if (!isRecord(c))
|
|
78
|
+
return null;
|
|
79
|
+
if (typeof c["id"] !== "string" || typeof c["kind"] !== "string" || typeof c["claim"] !== "string")
|
|
80
|
+
return null;
|
|
81
|
+
if (c["subject"] !== undefined && typeof c["subject"] !== "string")
|
|
82
|
+
return null;
|
|
83
|
+
if (typeof c["verdict"] !== "string" || !VERDICTS.includes(c["verdict"]))
|
|
84
|
+
return null;
|
|
85
|
+
// The frozen view iterates evidence per claim — a scalar here would crash it.
|
|
86
|
+
if (!Array.isArray(c["evidence"]) || c["evidence"].some((e) => typeof e !== "string"))
|
|
87
|
+
return null;
|
|
88
|
+
if (c["note"] !== undefined && typeof c["note"] !== "string")
|
|
89
|
+
return null;
|
|
90
|
+
if (c["answered_by"] !== undefined && typeof c["answered_by"] !== "string")
|
|
91
|
+
return null;
|
|
92
|
+
// Absent fine, present whole — the isConsumedContractReport precedent, and
|
|
93
|
+
// the guard body lives with the pin shape it vouches for (pins/pin.ts). A
|
|
94
|
+
// broken pins block makes the whole record unreadable and therefore never
|
|
95
|
+
// overwritten: skipping it instead would read a hand-damaged record as
|
|
96
|
+
// drift-free.
|
|
97
|
+
if (!isEvidencePinList(c["evidence_pins"]))
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
if (doc["schema"] !== undefined && doc["schema"] !== 2)
|
|
101
|
+
return null;
|
|
102
|
+
if (!isConsumedReport(doc["report"]))
|
|
103
|
+
return null;
|
|
104
|
+
if (!isConsumedContractReport(doc["contractReport"]))
|
|
105
|
+
return null;
|
|
106
|
+
if (doc["attestations"] !== undefined) {
|
|
107
|
+
if (!Array.isArray(doc["attestations"]))
|
|
108
|
+
return null;
|
|
109
|
+
for (const a of doc["attestations"]) {
|
|
110
|
+
if (!isRecord(a))
|
|
111
|
+
return null;
|
|
112
|
+
if (typeof a["service"] !== "string" || typeof a["commit"] !== "string" || typeof a["recorded"] !== "string")
|
|
113
|
+
return null;
|
|
114
|
+
if (!/^[0-9a-f]{40,64}$/i.test(a["commit"]))
|
|
115
|
+
return null;
|
|
116
|
+
if (!Array.isArray(a["claims"]) || a["claims"].some((id) => typeof id !== "string"))
|
|
117
|
+
return null;
|
|
118
|
+
if (!isConsumedReport(a["report"]))
|
|
119
|
+
return null;
|
|
120
|
+
if (!isConsumedContractReport(a["contractReport"]))
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return doc;
|
|
125
|
+
}
|
|
126
|
+
/** Absent is fine — most records answer nothing from a report. Present must be whole: it is printed. */
|
|
127
|
+
function isConsumedReport(v) {
|
|
128
|
+
if (v === undefined)
|
|
129
|
+
return true;
|
|
130
|
+
return (isRecord(v) &&
|
|
131
|
+
typeof v["path"] === "string" &&
|
|
132
|
+
typeof v["digest"] === "string" &&
|
|
133
|
+
typeof v["mtime"] === "string" &&
|
|
134
|
+
isCount(v["scenarios"]));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The contract report's pin, exactly as loose as {@link isConsumedReport}:
|
|
138
|
+
* absent fine, present whole — for the fields the readers dereference.
|
|
139
|
+
* `format` is OPTIONAL here even though loam always writes it: nothing in the
|
|
140
|
+
* readers dereferences it (it is provenance for a person, echoed only inside
|
|
141
|
+
* whole-object payloads), and the generated AGENTS.md teaches the pin as
|
|
142
|
+
* path/sha256/mtime/operation-count — so a hand-written block faithful to that
|
|
143
|
+
* teaching must not make the WHOLE record unreadable over a field no reader
|
|
144
|
+
* uses. When present it must be a string, and any string: a record written by
|
|
145
|
+
* a newer loam that learned a second report format must not read as
|
|
146
|
+
* unreadable here.
|
|
147
|
+
*/
|
|
148
|
+
function isConsumedContractReport(v) {
|
|
149
|
+
if (v === undefined)
|
|
150
|
+
return true;
|
|
151
|
+
return (isRecord(v) &&
|
|
152
|
+
typeof v["path"] === "string" &&
|
|
153
|
+
typeof v["digest"] === "string" &&
|
|
154
|
+
typeof v["mtime"] === "string" &&
|
|
155
|
+
isCount(v["operations"]) &&
|
|
156
|
+
(v["format"] === undefined || typeof v["format"] === "string"));
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* A summary count: an integer. YAML hands back strings for anything quoted.
|
|
160
|
+
* Negativity is deliberately NOT checked here: a negative count is a summary
|
|
161
|
+
* that disagrees with the claims below it, and `summaryDisagreement` owns that
|
|
162
|
+
* refusal — failing the SHAPE check instead made the message list five keys
|
|
163
|
+
* that were all present, while the miscount branch one call away holds the
|
|
164
|
+
* advice the case actually needs.
|
|
165
|
+
*/
|
|
166
|
+
function isCount(v) {
|
|
167
|
+
return typeof v === "number" && Number.isInteger(v);
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=shape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape.js","sourceRoot":"","sources":["../../../../src/core/verify/read/shape.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a file must hold to be a verification record — the shape check and the\n * self-consistency check every file of the record passes on the way in.\n *\n * Moved out of `./file.ts` when the record became several files: that module\n * now owns WHICH files make up a record and how a damaged one is reported,\n * while this one owns what any single file has to say to be read at all. The\n * two are phases — find the files, then grade each — and the grading never\n * needed to know where its bytes came from.\n */\nimport { isRecord } from \"../../kernel/records.js\";\nimport { VERDICTS } from \"../answers.js\";\nimport { isEvidencePinList } from \"../pins/pin.js\";\nimport { tallyAnswers, type Verification } from \"../record.js\";\n\n/**\n * Does the record's `summary` add up from its own `claims[]`?\n *\n * The summary is what wrote the file; the claims are what it answered. Nothing\n * ever compared them, so every reader that took the shortcut — verify's frozen\n * verdict for a shipped feature, list's column, status's block — reported a\n * record full of unconfirmed claims as fully confirmed. Believing either side\n * over the other would be a guess about which half was tampered with, so a\n * record that contradicts itself is refused like any other file loam cannot\n * use. The code is in the reason: it travels wherever the reason is printed.\n */\nexport function summaryDisagreement(v: Verification): string | null {\n const t = tallyAnswers(v.claims);\n const unanswered = v.summary.unanswered ?? 0;\n const says: string[] = [];\n // Before the arithmetic, because a negative count defeats it: `unanswered:\n // -2` is never something loam wrote, and it is the one value that lets a\n // hand-edited summary balance its own books — every comparison below then\n // agrees, and a record contradicting its claims reported `verified`.\n for (const [field, value] of Object.entries(v.summary)) {\n if (typeof value === \"number\" && value < 0) says.push(`carries a negative count (${field}: ${value})`);\n }\n if (v.summary.confirmed !== t.confirmed) {\n says.push(`says ${v.summary.confirmed} confirmed where claims[] holds ${t.confirmed}`);\n }\n if (v.summary.unconfirmed !== t.unconfirmed) {\n says.push(`says ${v.summary.unconfirmed} unconfirmed where claims[] holds ${t.unconfirmed}`);\n }\n if (v.summary.claims !== v.claims.length + unanswered) {\n says.push(\n `says ${v.summary.claims} claim(s)${unanswered === 0 ? \"\" : ` including ${unanswered} unanswered`} where claims[] holds ${v.claims.length}`,\n );\n }\n return says.length === 0\n ? null\n : `its summary ${says.join(\", \")} — the record contradicts itself, so neither half can be believed`;\n}\n\n/**\n * The shape check `readVerificationState` (`./file.ts`) stands behind:\n * everything its readers\n * dereference (verify's report/frozen views, list's verification column) must\n * be present and typed, or the cast would manufacture a Verification that\n * crashes them. Deliberately no tighter than the readers need: extra keys pass\n * (a human may annotate), `answered_by` stays optional (absent in records that\n * predate `--results`) and is any string, like `kind` — the readers only ever\n * compare them, and a record written by a newer loam must not read as absent.\n */\nexport function asVerification(doc: unknown): Verification | null {\n if (!isRecord(doc)) return null;\n const summary = doc[\"summary\"];\n if (\n typeof doc[\"feature\"] !== \"string\" ||\n typeof doc[\"recorded\"] !== \"string\" ||\n typeof doc[\"checklist\"] !== \"string\" ||\n !isRecord(summary) ||\n !isCount(summary[\"claims\"]) ||\n !isCount(summary[\"confirmed\"]) ||\n !isCount(summary[\"unconfirmed\"]) ||\n (summary[\"unanswered\"] !== undefined && !isCount(summary[\"unanswered\"])) ||\n !Array.isArray(doc[\"claims\"])\n ) {\n return null;\n }\n for (const c of doc[\"claims\"]) {\n if (!isRecord(c)) return null;\n if (typeof c[\"id\"] !== \"string\" || typeof c[\"kind\"] !== \"string\" || typeof c[\"claim\"] !== \"string\") return null;\n if (c[\"subject\"] !== undefined && typeof c[\"subject\"] !== \"string\") return null;\n if (typeof c[\"verdict\"] !== \"string\" || !(VERDICTS as readonly string[]).includes(c[\"verdict\"])) return null;\n // The frozen view iterates evidence per claim — a scalar here would crash it.\n if (!Array.isArray(c[\"evidence\"]) || c[\"evidence\"].some((e) => typeof e !== \"string\")) return null;\n if (c[\"note\"] !== undefined && typeof c[\"note\"] !== \"string\") return null;\n if (c[\"answered_by\"] !== undefined && typeof c[\"answered_by\"] !== \"string\") return null;\n // Absent fine, present whole — the isConsumedContractReport precedent, and\n // the guard body lives with the pin shape it vouches for (pins/pin.ts). A\n // broken pins block makes the whole record unreadable and therefore never\n // overwritten: skipping it instead would read a hand-damaged record as\n // drift-free.\n if (!isEvidencePinList(c[\"evidence_pins\"])) return null;\n }\n if (doc[\"schema\"] !== undefined && doc[\"schema\"] !== 2) return null;\n if (!isConsumedReport(doc[\"report\"])) return null;\n if (!isConsumedContractReport(doc[\"contractReport\"])) return null;\n if (doc[\"attestations\"] !== undefined) {\n if (!Array.isArray(doc[\"attestations\"])) return null;\n for (const a of doc[\"attestations\"]) {\n if (!isRecord(a)) return null;\n if (typeof a[\"service\"] !== \"string\" || typeof a[\"commit\"] !== \"string\" || typeof a[\"recorded\"] !== \"string\") return null;\n if (!/^[0-9a-f]{40,64}$/i.test(a[\"commit\"])) return null;\n if (!Array.isArray(a[\"claims\"]) || a[\"claims\"].some((id) => typeof id !== \"string\")) return null;\n if (!isConsumedReport(a[\"report\"])) return null;\n if (!isConsumedContractReport(a[\"contractReport\"])) return null;\n }\n }\n return doc as unknown as Verification;\n}\n\n/** Absent is fine — most records answer nothing from a report. Present must be whole: it is printed. */\nfunction isConsumedReport(v: unknown): boolean {\n if (v === undefined) return true;\n return (\n isRecord(v) &&\n typeof v[\"path\"] === \"string\" &&\n typeof v[\"digest\"] === \"string\" &&\n typeof v[\"mtime\"] === \"string\" &&\n isCount(v[\"scenarios\"])\n );\n}\n\n/**\n * The contract report's pin, exactly as loose as {@link isConsumedReport}:\n * absent fine, present whole — for the fields the readers dereference.\n * `format` is OPTIONAL here even though loam always writes it: nothing in the\n * readers dereferences it (it is provenance for a person, echoed only inside\n * whole-object payloads), and the generated AGENTS.md teaches the pin as\n * path/sha256/mtime/operation-count — so a hand-written block faithful to that\n * teaching must not make the WHOLE record unreadable over a field no reader\n * uses. When present it must be a string, and any string: a record written by\n * a newer loam that learned a second report format must not read as\n * unreadable here.\n */\nfunction isConsumedContractReport(v: unknown): boolean {\n if (v === undefined) return true;\n return (\n isRecord(v) &&\n typeof v[\"path\"] === \"string\" &&\n typeof v[\"digest\"] === \"string\" &&\n typeof v[\"mtime\"] === \"string\" &&\n isCount(v[\"operations\"]) &&\n (v[\"format\"] === undefined || typeof v[\"format\"] === \"string\")\n );\n}\n\n/**\n * A summary count: an integer. YAML hands back strings for anything quoted.\n * Negativity is deliberately NOT checked here: a negative count is a summary\n * that disagrees with the claims below it, and `summaryDisagreement` owns that\n * refusal — failing the SHAPE check instead made the message list five keys\n * that were all present, while the miscount branch one call away holds the\n * advice the case actually needs.\n */\nfunction isCount(v: unknown): v is number {\n return typeof v === \"number\" && Number.isInteger(v);\n}\n"]}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The record as the union of its files: the one-file `verification.yaml` and
|
|
3
|
+
* one `verification/<svc>.yaml` per attesting service, added up into the single
|
|
4
|
+
* `Verification` every reader already takes.
|
|
5
|
+
*
|
|
6
|
+
* Pure on purpose — `./file.ts` reads and grades the bytes, the writer in
|
|
7
|
+
* `commands/verify/record.ts` hands in the files as they will stand after its
|
|
8
|
+
* commit, and both get the same answer from the same arithmetic. A writer that
|
|
9
|
+
* computed the post-record verdict its own way would be the second opinion
|
|
10
|
+
* `../record.ts` exists to prevent; this way what `--record` prints is exactly
|
|
11
|
+
* what the next read will say.
|
|
12
|
+
*
|
|
13
|
+
* Nothing here reads a `summary` as fact. Each file's own `summary` was graded
|
|
14
|
+
* against its own `claims[]` on the way in, and the union's is recomputed here
|
|
15
|
+
* from the claims it actually counts — so the block that used to be the
|
|
16
|
+
* hottest conflict in a fleet's docs PRs is now derived, never merged.
|
|
17
|
+
*/
|
|
18
|
+
import { tallyAnswers } from "../record.js";
|
|
19
|
+
/**
|
|
20
|
+
* Add the files up. `services` are the graded per-service files, each a
|
|
21
|
+
* schema-2 record holding exactly its own service's attestation (the reader
|
|
22
|
+
* refuses one that does not). Null only when there is nothing at all.
|
|
23
|
+
*
|
|
24
|
+
* With no per-service file the one-file record is returned VERBATIM — a record
|
|
25
|
+
* written before the directory existed, or by the all-at-once form, reads
|
|
26
|
+
* exactly as it always did, retained answers and all.
|
|
27
|
+
*
|
|
28
|
+
* Otherwise the record answers ONE version of the checklist. A file that
|
|
29
|
+
* answered a different version keeps its attestation on the list — that is
|
|
30
|
+
* what `verify.checklist-forked` reads to name it — but its answers do not
|
|
31
|
+
* count. The single shared file used to settle this by rewriting: the last
|
|
32
|
+
* writer pruned everybody's answers to ITS checklist. Per-service files are
|
|
33
|
+
* never rewritten by anyone else, so nothing can prune them, and counting an
|
|
34
|
+
* answer filed against another question set would let an old id that the
|
|
35
|
+
* feature no longer asks fill the slot of a new one it does — a record reading
|
|
36
|
+
* `verified` over a question nobody answered. Excluding is the honest half:
|
|
37
|
+
* the service behind is named, and recording it again is the whole fix.
|
|
38
|
+
*
|
|
39
|
+
* WHICH version is `live`'s to say whenever the caller knows it: the digest of
|
|
40
|
+
* the checklist the feature asks now (`loam verify`, its writer, `tasks`, and
|
|
41
|
+
* `status`/`list`/`gate` for an in-flight feature all derive it anyway), or
|
|
42
|
+
* the one `loam archive` captured as it merged. The files that answered it
|
|
43
|
+
* count. Only when no caller knows — the frozen view of an archived feature
|
|
44
|
+
* with no capture — or when no file answered it does the newest file decide
|
|
45
|
+
* (by `recorded`, a later service id breaking a same-day tie, and any
|
|
46
|
+
* per-service file outranking the one-file record). Newest-by-date used to
|
|
47
|
+
* decide always, and `recorded` is a DATE: a service recording this morning
|
|
48
|
+
* against the current checklist lost the tie to a service with a later id that
|
|
49
|
+
* recorded an older checklist an hour before — or a day later, from a docs
|
|
50
|
+
* branch that was behind — so the current answers were dropped, every reader
|
|
51
|
+
* called the record stale, and the writer printed a tally its own answers were
|
|
52
|
+
* not in.
|
|
53
|
+
*/
|
|
54
|
+
export function assembleRecord(legacy, services, live) {
|
|
55
|
+
if (services.length === 0)
|
|
56
|
+
return legacy;
|
|
57
|
+
const filed = new Set(services.flatMap((s) => (s.attestations ?? []).map((a) => a.service)));
|
|
58
|
+
const parts = [...legacyRemnant(legacy, filed), ...services];
|
|
59
|
+
// The question being asked now, when somebody answered it; every part only
|
|
60
|
+
// when nobody did — then the record is stale under every reader whichever
|
|
61
|
+
// part represents it, and the newest is the one a reader recognises.
|
|
62
|
+
const answering = live === undefined ? [] : parts.filter((part) => part.checklist === live);
|
|
63
|
+
const pool = answering.length > 0 ? answering : parts;
|
|
64
|
+
// `>=` so a later part wins a same-day tie: services arrive sorted by id,
|
|
65
|
+
// and after the one-file remnant, which is exactly the precedence above.
|
|
66
|
+
const current = pool.reduce((newest, part) => (part.recorded >= newest.recorded ? part : newest));
|
|
67
|
+
const claims = parts.filter((part) => part.checklist === current.checklist).flatMap((part) => part.claims);
|
|
68
|
+
const tally = tallyAnswers(claims);
|
|
69
|
+
return {
|
|
70
|
+
schema: 2,
|
|
71
|
+
feature: current.feature,
|
|
72
|
+
recorded: current.recorded,
|
|
73
|
+
checklist: current.checklist,
|
|
74
|
+
summary: {
|
|
75
|
+
claims: current.summary.claims,
|
|
76
|
+
confirmed: tally.confirmed,
|
|
77
|
+
unconfirmed: tally.unconfirmed,
|
|
78
|
+
unanswered: current.summary.claims - claims.length,
|
|
79
|
+
},
|
|
80
|
+
claims,
|
|
81
|
+
attestations: parts.flatMap((part) => part.attestations ?? []).sort((a, b) => a.service.localeCompare(b.service)),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* What of the one-file record still counts once services have files of their
|
|
86
|
+
* own: the attestations of the services that have NOT re-recorded since, and
|
|
87
|
+
* their answers.
|
|
88
|
+
*
|
|
89
|
+
* A schema-1 (all-at-once) file contributes nothing. Its answers carry no
|
|
90
|
+
* commit attestation, so a federated record could never count them — the rule
|
|
91
|
+
* the first federated write applied when it used to overwrite the file, and the
|
|
92
|
+
* reason that write now deletes it. A schema-2 file's attestation for a service
|
|
93
|
+
* that has a file of its own is superseded by that file and drops out, answers
|
|
94
|
+
* with it; when every attestation in it is superseded, nothing in it counts.
|
|
95
|
+
*/
|
|
96
|
+
function legacyRemnant(legacy, filed) {
|
|
97
|
+
if (legacy === null || legacy.schema !== 2)
|
|
98
|
+
return [];
|
|
99
|
+
const attestations = (legacy.attestations ?? []).filter((a) => !filed.has(a.service));
|
|
100
|
+
if (attestations.length === 0)
|
|
101
|
+
return [];
|
|
102
|
+
const kept = new Set(attestations.map((a) => a.service));
|
|
103
|
+
return [{ ...legacy, attestations, claims: legacy.claims.filter((c) => c.subject !== undefined && kept.has(c.subject)) }];
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=union.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"union.js","sourceRoot":"","sources":["../../../../src/core/verify/read/union.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The record as the union of its files: the one-file `verification.yaml` and\n * one `verification/<svc>.yaml` per attesting service, added up into the single\n * `Verification` every reader already takes.\n *\n * Pure on purpose — `./file.ts` reads and grades the bytes, the writer in\n * `commands/verify/record.ts` hands in the files as they will stand after its\n * commit, and both get the same answer from the same arithmetic. A writer that\n * computed the post-record verdict its own way would be the second opinion\n * `../record.ts` exists to prevent; this way what `--record` prints is exactly\n * what the next read will say.\n *\n * Nothing here reads a `summary` as fact. Each file's own `summary` was graded\n * against its own `claims[]` on the way in, and the union's is recomputed here\n * from the claims it actually counts — so the block that used to be the\n * hottest conflict in a fleet's docs PRs is now derived, never merged.\n */\nimport { tallyAnswers, type Verification } from \"../record.js\";\n\n/**\n * Add the files up. `services` are the graded per-service files, each a\n * schema-2 record holding exactly its own service's attestation (the reader\n * refuses one that does not). Null only when there is nothing at all.\n *\n * With no per-service file the one-file record is returned VERBATIM — a record\n * written before the directory existed, or by the all-at-once form, reads\n * exactly as it always did, retained answers and all.\n *\n * Otherwise the record answers ONE version of the checklist. A file that\n * answered a different version keeps its attestation on the list — that is\n * what `verify.checklist-forked` reads to name it — but its answers do not\n * count. The single shared file used to settle this by rewriting: the last\n * writer pruned everybody's answers to ITS checklist. Per-service files are\n * never rewritten by anyone else, so nothing can prune them, and counting an\n * answer filed against another question set would let an old id that the\n * feature no longer asks fill the slot of a new one it does — a record reading\n * `verified` over a question nobody answered. Excluding is the honest half:\n * the service behind is named, and recording it again is the whole fix.\n *\n * WHICH version is `live`'s to say whenever the caller knows it: the digest of\n * the checklist the feature asks now (`loam verify`, its writer, `tasks`, and\n * `status`/`list`/`gate` for an in-flight feature all derive it anyway), or\n * the one `loam archive` captured as it merged. The files that answered it\n * count. Only when no caller knows — the frozen view of an archived feature\n * with no capture — or when no file answered it does the newest file decide\n * (by `recorded`, a later service id breaking a same-day tie, and any\n * per-service file outranking the one-file record). Newest-by-date used to\n * decide always, and `recorded` is a DATE: a service recording this morning\n * against the current checklist lost the tie to a service with a later id that\n * recorded an older checklist an hour before — or a day later, from a docs\n * branch that was behind — so the current answers were dropped, every reader\n * called the record stale, and the writer printed a tally its own answers were\n * not in.\n */\nexport function assembleRecord(\n legacy: Verification | null,\n services: readonly Verification[],\n live?: string,\n): Verification | null {\n if (services.length === 0) return legacy;\n const filed = new Set(services.flatMap((s) => (s.attestations ?? []).map((a) => a.service)));\n const parts = [...legacyRemnant(legacy, filed), ...services];\n // The question being asked now, when somebody answered it; every part only\n // when nobody did — then the record is stale under every reader whichever\n // part represents it, and the newest is the one a reader recognises.\n const answering = live === undefined ? [] : parts.filter((part) => part.checklist === live);\n const pool = answering.length > 0 ? answering : parts;\n // `>=` so a later part wins a same-day tie: services arrive sorted by id,\n // and after the one-file remnant, which is exactly the precedence above.\n const current = pool.reduce((newest, part) => (part.recorded >= newest.recorded ? part : newest));\n const claims = parts.filter((part) => part.checklist === current.checklist).flatMap((part) => part.claims);\n const tally = tallyAnswers(claims);\n return {\n schema: 2,\n feature: current.feature,\n recorded: current.recorded,\n checklist: current.checklist,\n summary: {\n claims: current.summary.claims,\n confirmed: tally.confirmed,\n unconfirmed: tally.unconfirmed,\n unanswered: current.summary.claims - claims.length,\n },\n claims,\n attestations: parts.flatMap((part) => part.attestations ?? []).sort((a, b) => a.service.localeCompare(b.service)),\n };\n}\n\n/**\n * What of the one-file record still counts once services have files of their\n * own: the attestations of the services that have NOT re-recorded since, and\n * their answers.\n *\n * A schema-1 (all-at-once) file contributes nothing. Its answers carry no\n * commit attestation, so a federated record could never count them — the rule\n * the first federated write applied when it used to overwrite the file, and the\n * reason that write now deletes it. A schema-2 file's attestation for a service\n * that has a file of its own is superseded by that file and drops out, answers\n * with it; when every attestation in it is superseded, nothing in it counts.\n */\nfunction legacyRemnant(legacy: Verification | null, filed: ReadonlySet<string>): Verification[] {\n if (legacy === null || legacy.schema !== 2) return [];\n const attestations = (legacy.attestations ?? []).filter((a) => !filed.has(a.service));\n if (attestations.length === 0) return [];\n const kept = new Set(attestations.map((a) => a.service));\n return [{ ...legacy, attestations, claims: legacy.claims.filter((c) => c.subject !== undefined && kept.has(c.subject)) }];\n}\n"]}
|