@ybotok/loam 0.2.0-alpha.6 → 0.2.0-alpha.9
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 +1477 -1
- package/COMPARISON.md +91 -3
- package/CONTRIBUTING.md +37 -0
- package/MIGRATING-from-OpenSpec.md +40 -22
- package/QUICKSTART.md +181 -9
- package/README.md +256 -57
- package/ROADMAP.md +108 -11
- package/SCHEMA.md +721 -130
- package/WORKFLOW.md +272 -62
- package/dist/cli.js +26 -8
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +5 -10
- package/dist/commands/adopt/adopt.js.map +1 -1
- package/dist/commands/archive/archive.js +15 -18
- package/dist/commands/archive/archive.js.map +1 -1
- package/dist/commands/archive/plan/contracts/asyncapi.js +27 -41
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
- 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 +25 -42
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/verdict-lines.js +41 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +3 -7
- package/dist/commands/archive/plan/gate.js.map +1 -1
- package/dist/commands/archive/plan/landscape.js +4 -4
- package/dist/commands/archive/plan/landscape.js.map +1 -1
- package/dist/commands/archive/plan/refusal.js +8 -18
- package/dist/commands/archive/plan/refusal.js.map +1 -1
- package/dist/commands/archive/plan/specs.js +134 -25
- package/dist/commands/archive/plan/specs.js.map +1 -1
- package/dist/commands/archive/plan/state.js +1 -0
- package/dist/commands/archive/plan/state.js.map +1 -1
- package/dist/commands/archive/run.js +82 -33
- package/dist/commands/archive/run.js.map +1 -1
- package/dist/commands/archive/shipped.js +49 -0
- package/dist/commands/archive/shipped.js.map +1 -0
- package/dist/commands/context/context.js +4 -7
- package/dist/commands/context/context.js.map +1 -1
- package/dist/commands/context/print.js +2 -2
- package/dist/commands/context/print.js.map +1 -1
- package/dist/commands/delta/delta.js +44 -12
- package/dist/commands/delta/delta.js.map +1 -1
- package/dist/commands/delta/print.js +1 -1
- package/dist/commands/delta/print.js.map +1 -1
- package/dist/commands/dependencies.js +3 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +20 -27
- package/dist/commands/diff/diff.js.map +1 -1
- package/dist/commands/explore.js +4 -8
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +45 -8
- package/dist/commands/gate/gate.js.map +1 -1
- package/dist/commands/gate/print.js +15 -3
- package/dist/commands/gate/print.js.map +1 -1
- package/dist/commands/gherkin/commit.js +13 -54
- package/dist/commands/gherkin/commit.js.map +1 -1
- package/dist/commands/gherkin/gherkin.js +36 -20
- package/dist/commands/gherkin/gherkin.js.map +1 -1
- package/dist/commands/gherkin/reconcile.js +10 -13
- package/dist/commands/gherkin/reconcile.js.map +1 -1
- package/dist/commands/init/first-hour.js +44 -0
- package/dist/commands/init/first-hour.js.map +1 -1
- package/dist/commands/init/init.js +18 -22
- package/dist/commands/init/init.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +4 -6
- package/dist/commands/list/campaign/campaign.js.map +1 -1
- package/dist/commands/list/json.js +0 -4
- package/dist/commands/list/json.js.map +1 -1
- package/dist/commands/list/list.js +38 -23
- package/dist/commands/list/list.js.map +1 -1
- package/dist/commands/list/print.js +38 -6
- package/dist/commands/list/print.js.map +1 -1
- 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 +28 -33
- package/dist/commands/list/views.js.map +1 -1
- package/dist/commands/mcp/dispatch.js +91 -36
- package/dist/commands/mcp/dispatch.js.map +1 -1
- package/dist/commands/mcp/mcp.js +56 -15
- package/dist/commands/mcp/mcp.js.map +1 -1
- package/dist/commands/mcp/serve.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/active.js +27 -22
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/target.js +2 -2
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/error.js +7 -8
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
- 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 +8 -22
- package/dist/commands/new/commit.js.map +1 -1
- package/dist/commands/new/modifies.js +206 -0
- package/dist/commands/new/modifies.js.map +1 -0
- package/dist/commands/new/new.js +59 -14
- package/dist/commands/new/new.js.map +1 -1
- package/dist/commands/new/templates.js +48 -124
- package/dist/commands/new/templates.js.map +1 -1
- package/dist/commands/open.js +3 -7
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/policy/gate.js +27 -0
- package/dist/commands/policy/gate.js.map +1 -1
- package/dist/commands/rebase/plan.js +2 -2
- package/dist/commands/rebase/plan.js.map +1 -1
- package/dist/commands/rebase/rebase.js +60 -35
- package/dist/commands/rebase/rebase.js.map +1 -1
- package/dist/commands/seed/commit.js +55 -26
- package/dist/commands/seed/commit.js.map +1 -1
- package/dist/commands/seed/plan.js +64 -13
- package/dist/commands/seed/plan.js.map +1 -1
- package/dist/commands/seed/seed.js +6 -9
- package/dist/commands/seed/seed.js.map +1 -1
- package/dist/commands/show/show.js +3 -7
- package/dist/commands/show/show.js.map +1 -1
- package/dist/commands/status/print.js +13 -4
- package/dist/commands/status/print.js.map +1 -1
- package/dist/commands/status/status.js +3 -7
- package/dist/commands/status/status.js.map +1 -1
- package/dist/commands/steps/steps.js +3 -7
- package/dist/commands/steps/steps.js.map +1 -1
- package/dist/commands/subsystem/edit.js +7 -7
- package/dist/commands/subsystem/edit.js.map +1 -1
- package/dist/commands/subsystem/move.js +60 -25
- package/dist/commands/subsystem/move.js.map +1 -1
- package/dist/commands/subsystem/query.js +2 -4
- package/dist/commands/subsystem/query.js.map +1 -1
- package/dist/commands/subsystem/subsystem.js +4 -7
- package/dist/commands/subsystem/subsystem.js.map +1 -1
- package/dist/commands/subsystem/sync.js +141 -71
- package/dist/commands/subsystem/sync.js.map +1 -1
- package/dist/commands/subsystem/txn/report.js +55 -5
- package/dist/commands/subsystem/txn/report.js.map +1 -1
- package/dist/commands/subsystem/txn/txn.js +121 -45
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +82 -12
- package/dist/commands/subsystem/txn/views.js.map +1 -1
- package/dist/commands/tasks/render.js +116 -0
- package/dist/commands/tasks/render.js.map +1 -0
- package/dist/commands/tasks/tasks.js +113 -0
- package/dist/commands/tasks/tasks.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +6 -9
- package/dist/commands/unarchive/manifest.js.map +1 -1
- package/dist/commands/unarchive/restore.js +17 -28
- package/dist/commands/unarchive/restore.js.map +1 -1
- package/dist/commands/unarchive/unarchive.js +22 -30
- package/dist/commands/unarchive/unarchive.js.map +1 -1
- package/dist/commands/validate/arch-coverage.js +6 -15
- package/dist/commands/validate/arch-coverage.js.map +1 -1
- package/dist/commands/validate/checks/fleet-shape.js +4 -49
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
- package/dist/commands/validate/checks/health-deps.js +9 -6
- package/dist/commands/validate/checks/health-deps.js.map +1 -1
- package/dist/commands/validate/checks/requirements.js +18 -8
- package/dist/commands/validate/checks/requirements.js.map +1 -1
- package/dist/commands/validate/checks/sources.js +6 -2
- package/dist/commands/validate/checks/sources.js.map +1 -1
- package/dist/commands/validate/feature.js +23 -19
- package/dist/commands/validate/feature.js.map +1 -1
- package/dist/commands/validate/fleet/census.js +26 -3
- package/dist/commands/validate/fleet/census.js.map +1 -1
- package/dist/commands/validate/fleet/landscape.js +35 -32
- package/dist/commands/validate/fleet/landscape.js.map +1 -1
- package/dist/commands/validate/fleet/map/attest.js +7 -12
- package/dist/commands/validate/fleet/map/attest.js.map +1 -1
- package/dist/commands/validate/fleet/map/bindings.js +16 -5
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -1
- package/dist/commands/validate/fleet/map/consumers.js +43 -122
- package/dist/commands/validate/fleet/map/consumers.js.map +1 -1
- package/dist/commands/validate/fleet/map/usecases.js +5 -11
- package/dist/commands/validate/fleet/map/usecases.js.map +1 -1
- package/dist/commands/validate/fleet/obligations.js +1 -5
- package/dist/commands/validate/fleet/obligations.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/print.js +5 -1
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
- package/dist/commands/validate/fleet/usecases/usecases.js +6 -4
- package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -1
- package/dist/commands/validate/fleet/views/ids.js +33 -7
- package/dist/commands/validate/fleet/views/ids.js.map +1 -1
- package/dist/commands/validate/fleet/views/root-documents.js +17 -35
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -1
- package/dist/commands/validate/fleet/views/stale.js +31 -14
- package/dist/commands/validate/fleet/views/stale.js.map +1 -1
- package/dist/commands/validate/links/corpus.js +1 -4
- package/dist/commands/validate/links/corpus.js.map +1 -1
- package/dist/commands/validate/links/glossary.js.map +1 -1
- package/dist/commands/validate/report.js +5 -4
- package/dist/commands/validate/report.js.map +1 -1
- package/dist/commands/validate/scope.js +113 -0
- package/dist/commands/validate/scope.js.map +1 -0
- package/dist/commands/validate/service/api.js +16 -104
- package/dist/commands/validate/service/api.js.map +1 -1
- 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 +1 -1
- package/dist/commands/validate/service/events/events.js.map +1 -1
- package/dist/commands/validate/service/events/message-producers.js +1 -1
- package/dist/commands/validate/service/events/message-producers.js.map +1 -1
- package/dist/commands/validate/service/evidence-pins.js +56 -10
- package/dist/commands/validate/service/evidence-pins.js.map +1 -1
- package/dist/commands/validate/service/service.js +16 -26
- package/dist/commands/validate/service/service.js.map +1 -1
- package/dist/commands/validate/service/specs.js +31 -38
- package/dist/commands/validate/service/specs.js.map +1 -1
- package/dist/commands/validate/service/spine.js +8 -11
- package/dist/commands/validate/service/spine.js.map +1 -1
- package/dist/commands/validate/service/usecases/usecases.js +3 -3
- package/dist/commands/validate/service/usecases/usecases.js.map +1 -1
- package/dist/commands/validate/validate.js +44 -26
- package/dist/commands/validate/validate.js.map +1 -1
- package/dist/commands/verify/cross/diff.js +2 -6
- package/dist/commands/verify/cross/diff.js.map +1 -1
- package/dist/commands/verify/evidence/contract.js +1 -1
- package/dist/commands/verify/evidence/contract.js.map +1 -1
- package/dist/commands/verify/evidence/read.js +12 -3
- package/dist/commands/verify/evidence/read.js.map +1 -1
- package/dist/commands/verify/frozen.js +45 -23
- package/dist/commands/verify/frozen.js.map +1 -1
- package/dist/commands/verify/record.js +69 -17
- package/dist/commands/verify/record.js.map +1 -1
- package/dist/commands/verify/report.js +27 -44
- package/dist/commands/verify/report.js.map +1 -1
- package/dist/commands/verify/results.js +112 -30
- package/dist/commands/verify/results.js.map +1 -1
- package/dist/commands/verify/verify.js +54 -51
- package/dist/commands/verify/verify.js.map +1 -1
- package/dist/commands/vouch/pack/pack.js +2 -2
- package/dist/commands/vouch/pack/pack.js.map +1 -1
- package/dist/commands/vouch/pack/print.js +1 -6
- package/dist/commands/vouch/pack/print.js.map +1 -1
- package/dist/commands/vouch/pack/sections.js +1 -2
- package/dist/commands/vouch/pack/sections.js.map +1 -1
- package/dist/commands/vouch/run.js +26 -81
- package/dist/commands/vouch/run.js.map +1 -1
- package/dist/commands/vouch/sample/plan.js +20 -34
- package/dist/commands/vouch/sample/plan.js.map +1 -1
- package/dist/commands/vouch/vet/verify.js +39 -7
- package/dist/commands/vouch/vet/verify.js.map +1 -1
- package/dist/commands/vouch/vouch.js +10 -17
- package/dist/commands/vouch/vouch.js.map +1 -1
- package/dist/core/agent/agents-md/command-map.js +57 -102
- package/dist/core/agent/agents-md/command-map.js.map +1 -1
- package/dist/core/agent/agents-md/map/explain.js +4 -3
- package/dist/core/agent/agents-md/map/explain.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
- package/dist/core/agent/agents-md/map/mcp.js +10 -7
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
- package/dist/core/agent/agents-md/map/status.js +9 -3
- package/dist/core/agent/agents-md/map/status.js.map +1 -1
- package/dist/core/agent/agents-md/map/subsystem.js +31 -17
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
- package/dist/core/agent/agents-md/map/usecases.js +24 -6
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
- package/dist/core/agent/agents-md/refusals.js +37 -21
- package/dist/core/agent/agents-md/refusals.js.map +1 -1
- package/dist/core/agent/agents-md.js +25 -10
- package/dist/core/agent/agents-md.js.map +1 -1
- package/dist/core/agent/agents-stamp.js +10 -0
- package/dist/core/agent/agents-stamp.js.map +1 -1
- package/dist/core/agent/scaffold.js +10 -15
- package/dist/core/agent/scaffold.js.map +1 -1
- package/dist/core/agent/tools/dialects.js +1 -0
- package/dist/core/agent/tools/dialects.js.map +1 -1
- package/dist/core/agent/tools/registry.js +41 -76
- package/dist/core/agent/tools/registry.js.map +1 -1
- package/dist/core/agent/workflows/adopt.js +5 -3
- package/dist/core/agent/workflows/adopt.js.map +1 -1
- package/dist/core/agent/workflows/check.js +25 -22
- package/dist/core/agent/workflows/check.js.map +1 -1
- package/dist/core/agent/workflows/closing.js +54 -21
- package/dist/core/agent/workflows/closing.js.map +1 -1
- package/dist/core/agent/workflows/feature.js +79 -11
- package/dist/core/agent/workflows/feature.js.map +1 -1
- package/dist/core/agent/workflows/implement.js +91 -13
- package/dist/core/agent/workflows/implement.js.map +1 -1
- package/dist/core/agent/workflows/reference/authoring.js +3 -1
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
- package/dist/core/agent/workflows/reference/codes.js +8 -6
- package/dist/core/agent/workflows/reference/codes.js.map +1 -1
- package/dist/core/agent/workflows/reference/done-check.js +37 -15
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
- package/dist/core/agent/workflows/reference/reference.js.map +1 -1
- package/dist/core/agent/workflows/reference/spine.js +62 -24
- package/dist/core/agent/workflows/reference/spine.js.map +1 -1
- package/dist/core/asyncapi/baseline/plan.js +14 -81
- package/dist/core/asyncapi/baseline/plan.js.map +1 -1
- package/dist/core/asyncapi/digest.js +30 -25
- package/dist/core/asyncapi/digest.js.map +1 -1
- package/dist/core/asyncapi/merge/components.js +5 -4
- package/dist/core/asyncapi/merge/components.js.map +1 -1
- package/dist/core/asyncapi/merge/error.js +7 -2
- package/dist/core/asyncapi/merge/error.js.map +1 -1
- package/dist/core/asyncapi/merge/markers.js +30 -50
- package/dist/core/asyncapi/merge/markers.js.map +1 -1
- package/dist/core/asyncapi/merge/merge.js +31 -53
- package/dist/core/asyncapi/merge/merge.js.map +1 -1
- package/dist/core/asyncapi/merge/pin.js +10 -36
- package/dist/core/asyncapi/merge/pin.js.map +1 -1
- package/dist/core/asyncapi/model.js.map +1 -1
- package/dist/core/asyncapi/read.js +36 -12
- package/dist/core/asyncapi/read.js.map +1 -1
- package/dist/core/brief/brief.js +30 -25
- package/dist/core/brief/brief.js.map +1 -1
- package/dist/core/brief/landscape.js +8 -12
- package/dist/core/brief/landscape.js.map +1 -1
- package/dist/core/brief/map/extend.js +8 -5
- package/dist/core/brief/map/extend.js.map +1 -1
- package/dist/core/brief/map/owed.js +8 -26
- package/dist/core/brief/map/owed.js.map +1 -1
- package/dist/core/brief/targets.js +79 -5
- package/dist/core/brief/targets.js.map +1 -1
- package/dist/core/brief/unchecked.js +1 -1
- package/dist/core/brief/unchecked.js.map +1 -1
- package/dist/core/c4/likec4.js +147 -16
- package/dist/core/c4/likec4.js.map +1 -1
- package/dist/core/c4/locate/unlocated.js +150 -0
- package/dist/core/c4/locate/unlocated.js.map +1 -0
- package/dist/core/c4/parsed/values.js +54 -1
- package/dist/core/c4/parsed/values.js.map +1 -1
- package/dist/core/c4/prefetch/fleet.js +1 -9
- package/dist/core/c4/prefetch/fleet.js.map +1 -1
- package/dist/core/c4/project/architecture.js +31 -9
- package/dist/core/c4/project/architecture.js.map +1 -1
- package/dist/core/c4/project/documents.js +36 -10
- package/dist/core/c4/project/documents.js.map +1 -1
- package/dist/core/c4/project/load.js +14 -21
- package/dist/core/c4/project/load.js.map +1 -1
- package/dist/core/c4/project/staged.js +7 -3
- package/dist/core/c4/project/staged.js.map +1 -1
- 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 +25 -1
- package/dist/core/c4/resolve/service.js.map +1 -1
- package/dist/core/c4/seed/template.js +15 -1
- package/dist/core/c4/seed/template.js.map +1 -1
- package/dist/core/c4/service-model/shape.js +2 -22
- package/dist/core/c4/service-model/shape.js.map +1 -1
- package/dist/core/c4/source-mask.js +288 -4
- package/dist/core/c4/source-mask.js.map +1 -1
- package/dist/core/c4/source-scan.js +203 -147
- package/dist/core/c4/source-scan.js.map +1 -1
- package/dist/core/c4/splice/authored-source.js +19 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -1
- package/dist/core/c4/splice/delta-blocks.js +5 -18
- package/dist/core/c4/splice/delta-blocks.js.map +1 -1
- package/dist/core/c4/splice/landscape-merge.js +13 -8
- package/dist/core/c4/splice/landscape-merge.js.map +1 -1
- package/dist/core/c4/splice/model/merge.js +10 -6
- package/dist/core/c4/splice/model/merge.js.map +1 -1
- package/dist/core/c4/splice/placement.js +108 -18
- package/dist/core/c4/splice/placement.js.map +1 -1
- package/dist/core/c4/workspace.js +13 -22
- package/dist/core/c4/workspace.js.map +1 -1
- package/dist/core/capabilities/capabilities.js +6 -6
- package/dist/core/capabilities/capabilities.js.map +1 -1
- package/dist/core/capabilities/delta/doc.js +7 -10
- package/dist/core/capabilities/delta/doc.js.map +1 -1
- package/dist/core/capabilities/findings.js +15 -20
- package/dist/core/capabilities/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/findings.js +12 -19
- package/dist/core/capabilities/realizes/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/join.js +0 -20
- package/dist/core/capabilities/realizes/join.js.map +1 -1
- package/dist/core/capabilities/rollup.js +6 -0
- package/dist/core/capabilities/rollup.js.map +1 -1
- package/dist/core/capabilities/tree.js +6 -28
- package/dist/core/capabilities/tree.js.map +1 -1
- package/dist/core/capabilities/usecase-join.js +3 -13
- package/dist/core/capabilities/usecase-join.js.map +1 -1
- package/dist/core/coherence/authoring/scaffold.js +11 -3
- package/dist/core/coherence/authoring/scaffold.js.map +1 -1
- package/dist/core/coherence/authoring/sentinels.js +2 -1
- package/dist/core/coherence/authoring/sentinels.js.map +1 -1
- package/dist/core/coherence/coherence.js +30 -16
- package/dist/core/coherence/coherence.js.map +1 -1
- 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/declared.js +60 -34
- package/dist/core/coherence/declared.js.map +1 -1
- package/dist/core/coherence/events/declared.js +44 -59
- package/dist/core/coherence/events/declared.js.map +1 -1
- package/dist/core/coherence/events/events.js +119 -8
- package/dist/core/coherence/events/events.js.map +1 -1
- package/dist/core/coherence/events/grades.js +31 -8
- package/dist/core/coherence/events/grades.js.map +1 -1
- package/dist/core/coherence/events/lookups.js +14 -82
- package/dist/core/coherence/events/lookups.js.map +1 -1
- package/dist/core/coherence/lookups.js +18 -78
- package/dist/core/coherence/lookups.js.map +1 -1
- 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/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/delta.js +1 -1
- package/dist/core/delta/delta.js.map +1 -1
- package/dist/core/deployment/delta.js +15 -16
- package/dist/core/deployment/delta.js.map +1 -1
- package/dist/core/diff/base-git.js +94 -12
- package/dist/core/diff/base-git.js.map +1 -1
- package/dist/core/diff/base-state.js +28 -16
- package/dist/core/diff/base-state.js.map +1 -1
- 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 +22 -6
- package/dist/core/diff/scope/changed-paths.js.map +1 -1
- 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 +100 -31
- package/dist/core/diff/semantic.js.map +1 -1
- package/dist/core/doctor/agents.js +34 -18
- package/dist/core/doctor/agents.js.map +1 -1
- package/dist/core/doctor/report.js.map +1 -1
- package/dist/core/document/apply.js +10 -2
- package/dist/core/document/apply.js.map +1 -1
- package/dist/core/document/frontmatter.js +55 -0
- package/dist/core/document/frontmatter.js.map +1 -1
- package/dist/core/document/parse.js +29 -6
- package/dist/core/document/parse.js.map +1 -1
- package/dist/core/document/scenarios.js +17 -0
- package/dist/core/document/scenarios.js.map +1 -1
- package/dist/core/document/spec.js +4 -11
- package/dist/core/document/spec.js.map +1 -1
- package/dist/core/envelope/config.js +10 -0
- package/dist/core/envelope/config.js.map +1 -1
- package/dist/core/envelope/json.js +34 -0
- package/dist/core/envelope/json.js.map +1 -1
- package/dist/core/explain/families.js +17 -12
- package/dist/core/explain/families.js.map +1 -1
- package/dist/core/explain/refusals.js +3 -3
- package/dist/core/explain/refusals.js.map +1 -1
- package/dist/core/explain/terms.js +18 -9
- package/dist/core/explain/terms.js.map +1 -1
- package/dist/core/explore/explore.js +2 -7
- package/dist/core/explore/explore.js.map +1 -1
- package/dist/core/fleet-context.js +34 -95
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +10 -3
- package/dist/core/gate/checks.js.map +1 -1
- package/dist/core/gate/partners.js +64 -19
- package/dist/core/gate/partners.js.map +1 -1
- package/dist/core/gate/report.js.map +1 -1
- package/dist/core/gate/verification.js +75 -12
- package/dist/core/gate/verification.js.map +1 -1
- package/dist/core/gherkin/stale.js +13 -37
- package/dist/core/gherkin/stale.js.map +1 -1
- package/dist/core/glossary/tree.js +6 -19
- package/dist/core/glossary/tree.js.map +1 -1
- package/dist/core/kernel/concurrency.js +19 -0
- package/dist/core/kernel/concurrency.js.map +1 -1
- package/dist/core/kernel/records.js +11 -0
- package/dist/core/kernel/records.js.map +1 -1
- package/dist/core/links/backlinks.js +1 -3
- package/dist/core/links/backlinks.js.map +1 -1
- package/dist/core/links/corpus.js +5 -36
- package/dist/core/links/corpus.js.map +1 -1
- package/dist/core/mcp/argv.js +101 -0
- package/dist/core/mcp/argv.js.map +1 -1
- package/dist/core/mcp/author-tools.js +2 -1
- package/dist/core/mcp/author-tools.js.map +1 -1
- package/dist/core/mcp/protocol.js +2 -2
- package/dist/core/mcp/protocol.js.map +1 -1
- package/dist/core/mcp/tools.js +43 -33
- package/dist/core/mcp/tools.js.map +1 -1
- package/dist/core/openapi/baseline/gate.js +53 -12
- package/dist/core/openapi/baseline/gate.js.map +1 -1
- package/dist/core/openapi/baseline/plan.js +22 -8
- package/dist/core/openapi/baseline/plan.js.map +1 -1
- package/dist/core/openapi/baseline/record.js +0 -1
- package/dist/core/openapi/baseline/record.js.map +1 -1
- 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 +56 -0
- package/dist/core/openapi/contract-tree.js.map +1 -0
- package/dist/core/openapi/depth.js +38 -24
- package/dist/core/openapi/depth.js.map +1 -1
- package/dist/core/openapi/digest.js +52 -2
- package/dist/core/openapi/digest.js.map +1 -1
- package/dist/core/openapi/doc.js +12 -4
- package/dist/core/openapi/doc.js.map +1 -1
- package/dist/core/openapi/merge/components.js +25 -60
- package/dist/core/openapi/merge/components.js.map +1 -1
- package/dist/core/openapi/merge/error.js +3 -13
- package/dist/core/openapi/merge/error.js.map +1 -1
- package/dist/core/openapi/merge/markers.js +26 -30
- package/dist/core/openapi/merge/markers.js.map +1 -1
- package/dist/core/openapi/merge/merge.js +26 -52
- package/dist/core/openapi/merge/merge.js.map +1 -1
- package/dist/core/openapi/merge/pin.js +15 -35
- package/dist/core/openapi/merge/pin.js.map +1 -1
- package/dist/core/openspec/decide/verdict.js.map +1 -1
- package/dist/core/openspec/inventory.js +4 -5
- package/dist/core/openspec/inventory.js.map +1 -1
- package/dist/core/openspec/model/mapping.js +30 -28
- package/dist/core/openspec/model/mapping.js.map +1 -1
- package/dist/core/openspec/model/model.js +12 -7
- package/dist/core/openspec/model/model.js.map +1 -1
- package/dist/core/openspec/scan/changes.js +10 -4
- package/dist/core/openspec/scan/changes.js.map +1 -1
- package/dist/core/openspec/scan/config.js +7 -16
- package/dist/core/openspec/scan/config.js.map +1 -1
- package/dist/core/openspec/scan/shape.js +63 -19
- package/dist/core/openspec/scan/shape.js.map +1 -1
- package/dist/core/openspec/scan/walk.js +21 -38
- package/dist/core/openspec/scan/walk.js.map +1 -1
- package/dist/core/pack/living.js +2 -16
- package/dist/core/pack/living.js.map +1 -1
- package/dist/core/provenance/findings.js +48 -12
- package/dist/core/provenance/findings.js.map +1 -1
- package/dist/core/provenance/git.js +33 -57
- package/dist/core/provenance/git.js.map +1 -1
- package/dist/core/provenance/gitq/moves.js +71 -114
- package/dist/core/provenance/gitq/moves.js.map +1 -1
- package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
- package/dist/core/provenance/sources.js +23 -6
- package/dist/core/provenance/sources.js.map +1 -1
- package/dist/core/provenance/stamp.js +5 -17
- package/dist/core/provenance/stamp.js.map +1 -1
- package/dist/core/repo/entries.js.map +1 -1
- package/dist/core/repo/paths.js +34 -29
- package/dist/core/repo/paths.js.map +1 -1
- package/dist/core/repo/repo.js +24 -14
- package/dist/core/repo/repo.js.map +1 -1
- package/dist/core/repo/tree/find.js +12 -3
- package/dist/core/repo/tree/find.js.map +1 -1
- package/dist/core/repo/tree/fs.js +27 -1
- package/dist/core/repo/tree/fs.js.map +1 -1
- package/dist/core/repo/tree/render/body.js +45 -6
- package/dist/core/repo/tree/render/body.js.map +1 -1
- 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 +19 -2
- package/dist/core/repo/tree/render/stale.js.map +1 -1
- package/dist/core/repo/tree/render/views.js +102 -33
- package/dist/core/repo/tree/render/views.js.map +1 -1
- package/dist/core/repo/tree/walk.js +8 -30
- package/dist/core/repo/tree/walk.js.map +1 -1
- package/dist/core/results.js +47 -12
- package/dist/core/results.js.map +1 -1
- package/dist/core/staging/commit.js +10 -12
- package/dist/core/staging/commit.js.map +1 -1
- package/dist/core/staging/lock.js +74 -12
- package/dist/core/staging/lock.js.map +1 -1
- package/dist/core/staging/recovery/intent.js +2 -5
- package/dist/core/staging/recovery/intent.js.map +1 -1
- package/dist/core/staging/recovery/recover.js +60 -1
- package/dist/core/staging/recovery/recover.js.map +1 -1
- package/dist/core/staging/snapshot/manifest.js +152 -0
- package/dist/core/staging/snapshot/manifest.js.map +1 -0
- package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +10 -34
- package/dist/core/staging/snapshot/snapshot.js.map +1 -0
- package/dist/core/status/actions/execution.js +11 -0
- package/dist/core/status/actions/execution.js.map +1 -1
- package/dist/core/status/feature/artifacts.js +2 -2
- package/dist/core/status/feature/artifacts.js.map +1 -1
- package/dist/core/status/feature/feature.js +5 -11
- package/dist/core/status/feature/feature.js.map +1 -1
- package/dist/core/status/fleet/fleet.js +6 -2
- package/dist/core/status/fleet/fleet.js.map +1 -1
- package/dist/core/status/fleet/next.js +26 -3
- package/dist/core/status/fleet/next.js.map +1 -1
- package/dist/core/status/report.js.map +1 -1
- package/dist/core/status/verification.js +30 -15
- package/dist/core/status/verification.js.map +1 -1
- package/dist/core/tasks/packet.js +174 -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 +11 -14
- package/dist/core/usecases/capability.js.map +1 -1
- package/dist/core/usecases/delta/flows.js +5 -7
- package/dist/core/usecases/delta/flows.js.map +1 -1
- package/dist/core/usecases/fleet.js +178 -31
- package/dist/core/usecases/fleet.js.map +1 -1
- package/dist/core/usecases/operations.js +5 -4
- package/dist/core/usecases/operations.js.map +1 -1
- package/dist/core/usecases/service/flows.js +9 -6
- package/dist/core/usecases/service/flows.js.map +1 -1
- 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/verify/answers.js +12 -5
- package/dist/core/verify/answers.js.map +1 -1
- package/dist/core/verify/build.js +102 -82
- package/dist/core/verify/build.js.map +1 -1
- package/dist/core/verify/checklist.js +45 -10
- package/dist/core/verify/checklist.js.map +1 -1
- package/dist/core/verify/location.js +67 -0
- package/dist/core/verify/location.js.map +1 -0
- package/dist/core/verify/pins/lint.js +3 -2
- package/dist/core/verify/pins/lint.js.map +1 -1
- package/dist/core/verify/pins/pin.js +1 -1
- package/dist/core/verify/pins/pin.js.map +1 -1
- package/dist/core/verify/read/file.js +224 -0
- package/dist/core/verify/read/file.js.map +1 -0
- package/dist/core/verify/{file.js → read/shape.js} +16 -104
- 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 +35 -12
- package/dist/core/verify/record.js.map +1 -1
- package/dist/core/verify/store/commit.js +40 -30
- package/dist/core/verify/store/commit.js.map +1 -1
- package/dist/core/verify/store/render.js +8 -4
- package/dist/core/verify/store/render.js.map +1 -1
- package/dist/core/vocabulary/issue.js +1 -0
- package/dist/core/vocabulary/issue.js.map +1 -1
- package/dist/core/vocabulary/maturity.js +53 -3
- package/dist/core/vocabulary/maturity.js.map +1 -1
- package/examples/README.md +28 -9
- package/examples/docs/architecture/landscape.likec4 +21 -7
- package/examples/docs/architecture/services.likec4 +70 -0
- package/examples/docs/architecture/subsystems.likec4 +3 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +6 -2
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
- package/examples/docs/services/order-service/asyncapi.yaml +3 -2
- package/examples/docs/services/order-service/model.likec4 +10 -8
- package/examples/docs/services/payment-service/model.likec4 +6 -4
- package/examples/docs/services/platform/identity-service/model.likec4 +9 -7
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
- package/examples/docs/services/platform/notification-service/model.likec4 +4 -5
- package/package.json +1 -1
- package/dist/commands/validate/checks/vocabulary.js +0 -16
- package/dist/commands/validate/checks/vocabulary.js.map +0 -1
- package/dist/core/agent/agents-md/artifacts.js +0 -221
- package/dist/core/agent/agents-md/artifacts.js.map +0 -1
- package/dist/core/agent/agents-md/cycle.js +0 -180
- package/dist/core/agent/agents-md/cycle.js.map +0 -1
- package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
- package/dist/core/agent/agents-md/spine.js +0 -40
- package/dist/core/agent/agents-md/spine.js.map +0 -1
- package/dist/core/coherence/events/merged.js +0 -40
- package/dist/core/coherence/events/merged.js.map +0 -1
- package/dist/core/diff/current-state.js +0 -96
- package/dist/core/diff/current-state.js.map +0 -1
- package/dist/core/diff/victims.js +0 -256
- package/dist/core/diff/victims.js.map +0 -1
- package/dist/core/staging/snapshot.js.map +0 -1
- package/dist/core/verify/file.js.map +0 -1
|
@@ -12,9 +12,25 @@ import { emitJson, repoPath } from "../../core/envelope/json.js";
|
|
|
12
12
|
import { contestedDigests } from "../../core/results.js";
|
|
13
13
|
import {} from "../../core/verify/checklist.js";
|
|
14
14
|
import { contestedOperations } from "../../core/verify/evidence/contract.js";
|
|
15
|
-
import { attestedNotice, openClaimsNotice, tallyRecord,
|
|
15
|
+
import { attestedNotice, openClaimsNotice, tallyRecord, verificationVerdict, } from "../../core/verify/record.js";
|
|
16
|
+
import { recordLocation } from "../../core/verify/location.js";
|
|
16
17
|
import { plural } from "../policy/format.js";
|
|
17
18
|
import {} from "./report.js";
|
|
19
|
+
/**
|
|
20
|
+
* The frozen view: an archived feature's record, verbatim. No checklist is
|
|
21
|
+
* derived and no staleness is judged — there is nothing current to judge
|
|
22
|
+
* against, and pretending otherwise is how a true record reads as a lie.
|
|
23
|
+
* `frozen` is the marker a consumer branches on.
|
|
24
|
+
*
|
|
25
|
+
* The verdict is recounted from `claims[]`, never taken from `summary`: this is
|
|
26
|
+
* the post-ship verdict on a feature nobody can re-derive a checklist for, so a
|
|
27
|
+
* record whose counts had drifted from its own answers reported a shipped
|
|
28
|
+
* feature as fully confirmed. (A record that contradicts itself never reaches
|
|
29
|
+
* here — `readVerificationState` refuses it — so the recount and the summary
|
|
30
|
+
* agree; the recount is what says WHY.) No record at all is reported as exactly
|
|
31
|
+
* that, with `summary: null`, not zero claims, which would falsely say the
|
|
32
|
+
* feature promised nothing.
|
|
33
|
+
*/
|
|
18
34
|
export function reportFrozen(target, featureId, v) {
|
|
19
35
|
const { docsDir, featureDir, json } = target;
|
|
20
36
|
const tally = v === null ? null : tallyRecord(v);
|
|
@@ -34,16 +50,7 @@ export function reportFrozen(target, featureId, v) {
|
|
|
34
50
|
verdict,
|
|
35
51
|
attested: tally?.attested ?? 0,
|
|
36
52
|
summary: v === null ? null : v.summary,
|
|
37
|
-
recorded: v === null
|
|
38
|
-
? null
|
|
39
|
-
: {
|
|
40
|
-
path: repoPath(docsDir, verificationPath(featureDir)),
|
|
41
|
-
recorded: v.recorded,
|
|
42
|
-
checklist: v.checklist,
|
|
43
|
-
...(v.attestations === undefined ? {} : { attestations: v.attestations }),
|
|
44
|
-
...(v.report === undefined ? {} : { report: v.report }),
|
|
45
|
-
...(v.contractReport === undefined ? {} : { contractReport: v.contractReport }),
|
|
46
|
-
},
|
|
53
|
+
recorded: v === null ? null : recordedPayload(target, v),
|
|
47
54
|
claims: v === null ? [] : v.claims,
|
|
48
55
|
...(notices.length === 0 ? {} : { notices }),
|
|
49
56
|
});
|
|
@@ -83,6 +90,23 @@ export function reportFrozen(target, featureId, v) {
|
|
|
83
90
|
console.log(` ⚠ ${notice.code}: ${notice.message}`);
|
|
84
91
|
}
|
|
85
92
|
export const MARK = { confirmed: "✓", unconfirmed: "✗", unanswered: "?" };
|
|
93
|
+
/**
|
|
94
|
+
* The `recorded` block both views emit: where the record is, when it was
|
|
95
|
+
* written, which checklist it answered, and the evidence provenance it carries.
|
|
96
|
+
* `extra` lands right after `checklist` — the read view's `stale` — so each
|
|
97
|
+
* payload keeps the key order it has always had.
|
|
98
|
+
*/
|
|
99
|
+
export function recordedPayload(target, v, extra = {}) {
|
|
100
|
+
return {
|
|
101
|
+
path: repoPath(target.docsDir, recordLocation(target.featureDir)),
|
|
102
|
+
recorded: v.recorded,
|
|
103
|
+
checklist: v.checklist,
|
|
104
|
+
...extra,
|
|
105
|
+
...(v.attestations === undefined ? {} : { attestations: v.attestations }),
|
|
106
|
+
...(v.report === undefined ? {} : { report: v.report }),
|
|
107
|
+
...(v.contractReport === undefined ? {} : { contractReport: v.contractReport }),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
86
110
|
/**
|
|
87
111
|
* Who answered, where that changes what the verdict means. `[runner]` is a
|
|
88
112
|
* digest-matched green run; `[contract]` is an operationId-matched green
|
|
@@ -129,6 +153,14 @@ export function noticesFor(claims, feature) {
|
|
|
129
153
|
* field are excluded rather than counted as a third version: they make no claim
|
|
130
154
|
* about what they answered, and reading silence as disagreement would fire this
|
|
131
155
|
* on every record written before the field existed.
|
|
156
|
+
*
|
|
157
|
+
* In a record kept as per-service files the fork also decides what counts:
|
|
158
|
+
* the union counts only the files that answered its newest version
|
|
159
|
+
* (`core/verify/read/union.ts` says why), so the services named here are the
|
|
160
|
+
* ones whose answers are left out until they record again. In a one-file
|
|
161
|
+
* record the last writer had already pruned their stale answers, and the rest
|
|
162
|
+
* still count — which is why the sentence below says only what is true of
|
|
163
|
+
* both.
|
|
132
164
|
*/
|
|
133
165
|
export function forkedChecklistNotices(recorded, current) {
|
|
134
166
|
const stated = (recorded?.attestations ?? []).filter((a) => a.checklist !== undefined);
|
|
@@ -150,8 +182,8 @@ export function forkedChecklistNotices(recorded, current) {
|
|
|
150
182
|
message: `${plural(behind.length, "service")} answered a different version of this feature's question set ` +
|
|
151
183
|
`than the one it now asks (${current}): ${byVersion.join("; ")}. ` +
|
|
152
184
|
"The feature changed after they recorded, so their answers are not about the questions being asked now. " +
|
|
153
|
-
"Re-record them from their own repositories (`loam verify <FEAT> --service <id> --record`)
|
|
154
|
-
"
|
|
185
|
+
"Re-record them from their own repositories (`loam verify <FEAT> --service <id> --record`) — " +
|
|
186
|
+
"nothing gates on it, but until they do, the record does not answer what the feature asks.",
|
|
155
187
|
},
|
|
156
188
|
];
|
|
157
189
|
}
|
|
@@ -228,14 +260,4 @@ export function reportLine(r) {
|
|
|
228
260
|
export function contractReportLine(r) {
|
|
229
261
|
return `${r.path} (sha256 ${r.digest.slice(0, 12)}…, ${plural(r.operations, "operation")}, written ${r.mtime})`;
|
|
230
262
|
}
|
|
231
|
-
/* ------------------------------------------------------------------ */
|
|
232
|
-
/* Recording */
|
|
233
|
-
/* ------------------------------------------------------------------ */
|
|
234
|
-
/**
|
|
235
|
-
* `repoDir` is the repository an attestation is ABOUT: the commit it names, the
|
|
236
|
-
* tree its evidence must resolve inside, and where a `--results` report has to
|
|
237
|
-
* live. It is threaded rather than read here because it comes from
|
|
238
|
-
* `config.root`, and a `process.cwd()` in this function was the whole bug —
|
|
239
|
-
* from a subdirectory, "this repo" quietly became that subdirectory.
|
|
240
|
-
*/
|
|
241
263
|
//# sourceMappingURL=frozen.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frozen.js","sourceRoot":"","sources":["../../../src/commands/verify/frozen.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * An archived feature's record, read back and never recomputed.\n *\n * A frozen record is history: the checklist it answered no longer exists in\n * `features/`, so there is nothing to re-derive it from and re-deriving it would\n * be an opinion about a feature nobody can change. Everything here reads the\n * recorded claims, which is also why the shared marks (`answeredMark`), the\n * contested-claim notices and the report-pin lines live beside it: the read\n * and record views print the same facts about the same claims, in one voice.\n */\nimport { emitJson, repoPath } from \"../../core/envelope/json.js\";\nimport { contestedDigests } from \"../../core/results.js\";\nimport { type Claim } from \"../../core/verify/checklist.js\";\nimport { contestedOperations } from \"../../core/verify/evidence/contract.js\";\nimport {\n attestedNotice,\n openClaimsNotice,\n tallyRecord,\n type AnsweredClaim,\n type ConsumedContractReport,\n type ConsumedReport,\n type Verification,\n type VerifyNotice,\n verificationPath,\n verificationVerdict,\n} from \"../../core/verify/record.js\";\nimport { plural } from \"../policy/format.js\";\nimport { type VerifyTarget } from \"./report.js\";\n\nexport function reportFrozen(\n target: VerifyTarget,\n featureId: string,\n v: Verification | null,\n): void {\n const { docsDir, featureDir, json } = target;\n const tally = v === null ? null : tallyRecord(v);\n const verdict = tally === null ? \"unverified\" : verificationVerdict(tally);\n // The open-claims line rides into the frozen view too: a feature archived on\n // a partial record must keep saying so after it ships, not only before. No\n // record stays bare — the prose already says nothing was recorded.\n const open = tally === null ? null : openClaimsNotice(tally, featureId);\n const notices = [...(v === null ? [] : noticesFor(v.claims, featureId)), ...(open === null ? [] : [open])];\n\n if (json) {\n emitJson({\n command: \"verify\",\n feature: featureId,\n path: repoPath(docsDir, featureDir),\n frozen: true,\n verified: verdict === \"verified\",\n verdict,\n attested: tally?.attested ?? 0,\n summary: v === null ? null : v.summary,\n recorded:\n v === null\n ? null\n : {\n path: repoPath(docsDir, verificationPath(featureDir)),\n recorded: v.recorded,\n checklist: v.checklist,\n ...(v.attestations === undefined ? {} : { attestations: v.attestations }),\n ...(v.report === undefined ? {} : { report: v.report }),\n ...(v.contractReport === undefined ? {} : { contractReport: v.contractReport }),\n },\n claims: v === null ? [] : v.claims,\n ...(notices.length === 0 ? {} : { notices }),\n });\n return;\n }\n\n if (v === null) {\n console.log(\n `${featureId} is archived and has no verification record — nothing was recorded before it shipped.`,\n );\n return;\n }\n\n console.log(\n `${featureId} — verification recorded ${v.recorded}, frozen at archive (${repoPath(docsDir, featureDir)})\\n`,\n );\n for (const c of v.claims) {\n // `subject` is absent in records written before schema 2 — an old record\n // must read as itself, not gain a service it never named.\n const subject = c.subject === undefined ? \"\" : ` [${c.subject}]`;\n console.log(` ${MARK[c.verdict]} ${c.id}${subject} ${c.claim}${answeredMark(c)}`);\n for (const e of c.evidence) console.log(` ${e}`);\n if (c.note !== undefined) console.log(` note: ${c.note}`);\n }\n const counts = tallyRecord(v);\n console.log(`\\n Recorded ${v.recorded} — ${counts.confirmed} confirmed, ${counts.unconfirmed} unconfirmed.`);\n console.log(\n \" This checklist is frozen at record time: the feature is archived and its claims are not re-derived.\",\n );\n for (const attestation of v.attestations ?? []) {\n console.log(\n ` Attested by ${attestation.service} at ${attestation.commit.slice(0, 12)} (${attestation.recorded}).`,\n );\n if (attestation.report !== undefined) console.log(` from ${reportLine(attestation.report)}`);\n if (attestation.contractReport !== undefined) {\n console.log(` from ${contractReportLine(attestation.contractReport)}`);\n }\n }\n if (v.report !== undefined) console.log(` Answered from ${reportLine(v.report)}.`);\n if (v.contractReport !== undefined) console.log(` Contract report read: ${contractReportLine(v.contractReport)}.`);\n for (const notice of notices) console.log(` ⚠ ${notice.code}: ${notice.message}`);\n}\n\nexport const MARK: Record<string, string> = { confirmed: \"✓\", unconfirmed: \"✗\", unanswered: \"?\" };\n\n/**\n * Who answered, where that changes what the verdict means. `[runner]` is a\n * digest-matched green run; `[contract]` is an operationId-matched green\n * contract-test run — mechanical too, and marked so the record's provenance is\n * readable per line, not only in `contractReport:`; `[attested]` is a scenario\n * claim confirmed on somebody's word — the same ✓, a weaker fact, and it must\n * be visible on the line itself and not only in the summary. The attested test\n * runs FIRST and uses `attestedClaims`' own predicate (a confirmed scenario\n * claim not answered by the runner), so the mark can never disagree with the\n * tally: a scenario claim carrying `answered_by: external-runner` — a hand\n * edit, or a future loam; the reader accepts any string on purpose — COUNTS as\n * attested, and printing `[contract]` two lines above a notice saying \"an\n * agent's word\" would be the mark calling the tally a liar. Everything else is\n * unmarked: an agent's answer about a service existing or an operation being\n * exposed is exactly what the checklist asks for.\n */\nexport function answeredMark(c: AnsweredClaim): string {\n if (c.answered_by === \"runner\") return \" [runner]\";\n if (c.kind === \"scenario.tested\") {\n return c.verdict === \"confirmed\" ? \" [attested]\" : \"\";\n }\n return c.answered_by === \"external-runner\" ? \" [contract]\" : \"\";\n}\n\n/** Everything a verify surface has to say about a set of answers beyond the counts. */\nexport function noticesFor(claims: readonly AnsweredClaim[], feature: string): VerifyNotice[] {\n const attested = attestedNotice(claims, feature);\n return attested === null ? [] : [attested];\n}\n\n/**\n * `verify.checklist-forked` — two services answered DIFFERENT versions of this\n * feature{@link ServiceAttestation}'s question set.\n *\n * The record carries one top-level `checklist` digest, so before attestations\n * carried their own the file could not represent this at all: a service that\n * attested last week against a delta since rewritten wrote the same digest as\n * one that attested this morning, and the record-level staleness check flagged\n * both or neither. It could never say WHICH answers went stale, which is the\n * only question a reader has.\n *\n * A warn, and only ever a warn: a fork is normal mid-rollout — services attest\n * as they finish, and a feature legitimately changes between the first and the\n * last. What it must not do is stay invisible. Attestations with no `checklist`\n * field are excluded rather than counted as a third version: they make no claim\n * about what they answered, and reading silence as disagreement would fire this\n * on every record written before the field existed.\n */\nexport function forkedChecklistNotices(\n recorded: Verification | null,\n current: string,\n): VerifyNotice[] {\n const stated = (recorded?.attestations ?? []).filter((a) => a.checklist !== undefined);\n // Measured against the CURRENT checklist, not pairwise between attestations.\n // The pairwise reading answers the wrong question: three services that all\n // attested against a since-rewritten delta agree with each other perfectly\n // and are all stale, which pairwise calls clean. What a reader needs is\n // WHICH services answered something other than what is being asked now.\n const behind = stated.filter((a) => a.checklist !== current);\n if (behind.length === 0) return [];\n const byVersion = [...new Set(behind.map((a) => a.checklist!))]\n .sort()\n .map((v) => `${v} (${behind.filter((a) => a.checklist === v).map((a) => a.service).sort().join(\", \")})`);\n return [\n {\n code: \"verify.checklist-forked\",\n severity: \"warn\",\n message:\n `${plural(behind.length, \"service\")} answered a different version of this feature's question set ` +\n `than the one it now asks (${current}): ${byVersion.join(\"; \")}. ` +\n \"The feature changed after they recorded, so their answers are not about the questions being asked now. \" +\n \"Re-record them from their own repositories (`loam verify <FEAT> --service <id> --record`), \" +\n \"or accept the split knowingly — nothing gates on it.\",\n },\n ];\n}\n\n/**\n * The claims one report could not attribute: a scenario digest — and therefore\n * an `@loam-digest-…` tag — that more than one service claims.\n *\n * This is now a GUARD rather than a diagnosis of ordinary authoring. Until the\n * digest was salted by service, two services wording a scenario identically\n * really did share one tag, and this is what stopped one repository's green run\n * from confirming the other's claim. `scenarioBodyHash` folds the owning\n * service into the hash, so a claim's digest is computed from the same service\n * the claim is filed under and two subjects cannot share one short of a\n * truncated-sha256 collision. It stays because the invariant is worth asserting\n * where it is relied on: if this ever fires, a digest is answering for two\n * services and no report can say which, so nothing here may confirm.\n *\n * Derived from claims rather than from a report, so the read view raises it too\n * — the collision exists the moment the checklist does, and learning about it\n * from a `--results` run that already happened is learning too late.\n */\nexport function contestedNotices(claims: readonly Claim[]): VerifyNotice[] {\n const contested = contestedDigests(claims);\n if (contested.size === 0) return [];\n const shared = [...contested].map(([digest, services]) => `${digest} (${services.join(\", \")})`);\n return [\n {\n code: \"verify.digest-contested\",\n severity: \"warn\",\n message:\n `${plural(contested.size, \"scenario digest\")} claimed by more than one service: ${shared.join(\"; \")}. ` +\n \"The digest is salted by service, so this is a hash collision, not shared wording — but one report still \" +\n \"cannot say whose suite ran them, so a shared --results run leaves them unconfirmed. \" +\n \"Record each service's claims from its own repository with --service.\",\n claims: claims.filter((c) => c.digest !== undefined && contested.has(c.digest)).map((c) => c.id),\n },\n ];\n}\n\n/**\n * The digest-contested rule's twin on the contract axis: operationIds that\n * more than one service on this checklist exposes, which one report can\n * therefore never attribute (`contestedOperations`, the module that also\n * leaves those claims unconfirmed). Raised where the contract report is\n * consumed, so the record path says WHY the claims came back unconfirmed\n * instead of leaving two bare ✗ lines to be misread as failures.\n */\nexport function contestedOperationNotices(claims: readonly Claim[]): VerifyNotice[] {\n const contested = contestedOperations(claims);\n if (contested.size === 0) return [];\n const shared = [...contested].map(([operation, services]) => `${operation} (${services.join(\", \")})`);\n return [\n {\n code: \"verify.operation-contested\",\n severity: \"warn\",\n message:\n `${plural(contested.size, \"operationId\")} exposed by more than one service on this checklist: ${shared.join(\"; \")}. ` +\n \"A contract report entry names no service, so a shared --contract-results run cannot say whose suite exercised them and leaves them unconfirmed. \" +\n \"Record each service's claims from its own repository with --service.\",\n claims: claims.filter((c) => c.operation !== undefined && contested.has(c.operation)).map((c) => c.id),\n },\n ];\n}\n\n/** The report a `--results` run consumed, as one line a reviewer can check by hand. */\nexport function reportLine(r: ConsumedReport): string {\n return `${r.path} (sha256 ${r.digest.slice(0, 12)}…, ${plural(r.scenarios, \"tagged scenario\")}, written ${r.mtime})`;\n}\n\n/**\n * The contract report a `--contract-results` run consumed — the same line for\n * the same promise, counting operations where the cucumber one counts tagged\n * scenarios. A separate function rather than a parameter on `reportLine`\n * because the two pins are different shapes, and a merged formatter would need\n * exactly the switching flag that near-duplicates are kept apart to avoid.\n */\nexport function contractReportLine(r: ConsumedContractReport): string {\n return `${r.path} (sha256 ${r.digest.slice(0, 12)}…, ${plural(r.operations, \"operation\")}, written ${r.mtime})`;\n}\n\n\n/* ------------------------------------------------------------------ */\n/* Recording */\n/* ------------------------------------------------------------------ */\n\n/**\n * `repoDir` is the repository an attestation is ABOUT: the commit it names, the\n * tree its evidence must resolve inside, and where a `--results` report has to\n * live. It is threaded rather than read here because it comes from\n * `config.root`, and a `process.cwd()` in this function was the whole bug —\n * from a subdirectory, \"this repo\" quietly became that subdirectory.\n */\n"]}
|
|
1
|
+
{"version":3,"file":"frozen.js","sourceRoot":"","sources":["../../../src/commands/verify/frozen.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * An archived feature's record, read back and never recomputed.\n *\n * A frozen record is history: the checklist it answered no longer exists in\n * `features/`, so there is nothing to re-derive it from and re-deriving it would\n * be an opinion about a feature nobody can change. Everything here reads the\n * recorded claims, which is also why the shared marks (`answeredMark`), the\n * contested-claim notices and the report-pin lines live beside it: the read\n * and record views print the same facts about the same claims, in one voice.\n */\nimport { emitJson, repoPath } from \"../../core/envelope/json.js\";\nimport { contestedDigests } from \"../../core/results.js\";\nimport { type Claim } from \"../../core/verify/checklist.js\";\nimport { contestedOperations } from \"../../core/verify/evidence/contract.js\";\nimport {\n attestedNotice,\n openClaimsNotice,\n tallyRecord,\n type AnsweredClaim,\n type ConsumedContractReport,\n type ConsumedReport,\n type Verification,\n type VerifyNotice,\n verificationVerdict,\n} from \"../../core/verify/record.js\";\nimport { recordLocation } from \"../../core/verify/location.js\";\nimport { plural } from \"../policy/format.js\";\nimport { type VerifyTarget } from \"./report.js\";\n\n/**\n * The frozen view: an archived feature's record, verbatim. No checklist is\n * derived and no staleness is judged — there is nothing current to judge\n * against, and pretending otherwise is how a true record reads as a lie.\n * `frozen` is the marker a consumer branches on.\n *\n * The verdict is recounted from `claims[]`, never taken from `summary`: this is\n * the post-ship verdict on a feature nobody can re-derive a checklist for, so a\n * record whose counts had drifted from its own answers reported a shipped\n * feature as fully confirmed. (A record that contradicts itself never reaches\n * here — `readVerificationState` refuses it — so the recount and the summary\n * agree; the recount is what says WHY.) No record at all is reported as exactly\n * that, with `summary: null`, not zero claims, which would falsely say the\n * feature promised nothing.\n */\nexport function reportFrozen(\n target: VerifyTarget,\n featureId: string,\n v: Verification | null,\n): void {\n const { docsDir, featureDir, json } = target;\n const tally = v === null ? null : tallyRecord(v);\n const verdict = tally === null ? \"unverified\" : verificationVerdict(tally);\n // The open-claims line rides into the frozen view too: a feature archived on\n // a partial record must keep saying so after it ships, not only before. No\n // record stays bare — the prose already says nothing was recorded.\n const open = tally === null ? null : openClaimsNotice(tally, featureId);\n const notices = [...(v === null ? [] : noticesFor(v.claims, featureId)), ...(open === null ? [] : [open])];\n\n if (json) {\n emitJson({\n command: \"verify\",\n feature: featureId,\n path: repoPath(docsDir, featureDir),\n frozen: true,\n verified: verdict === \"verified\",\n verdict,\n attested: tally?.attested ?? 0,\n summary: v === null ? null : v.summary,\n recorded: v === null ? null : recordedPayload(target, v),\n claims: v === null ? [] : v.claims,\n ...(notices.length === 0 ? {} : { notices }),\n });\n return;\n }\n\n if (v === null) {\n console.log(\n `${featureId} is archived and has no verification record — nothing was recorded before it shipped.`,\n );\n return;\n }\n\n console.log(\n `${featureId} — verification recorded ${v.recorded}, frozen at archive (${repoPath(docsDir, featureDir)})\\n`,\n );\n for (const c of v.claims) {\n // `subject` is absent in records written before schema 2 — an old record\n // must read as itself, not gain a service it never named.\n const subject = c.subject === undefined ? \"\" : ` [${c.subject}]`;\n console.log(` ${MARK[c.verdict]} ${c.id}${subject} ${c.claim}${answeredMark(c)}`);\n for (const e of c.evidence) console.log(` ${e}`);\n if (c.note !== undefined) console.log(` note: ${c.note}`);\n }\n const counts = tallyRecord(v);\n console.log(`\\n Recorded ${v.recorded} — ${counts.confirmed} confirmed, ${counts.unconfirmed} unconfirmed.`);\n console.log(\n \" This checklist is frozen at record time: the feature is archived and its claims are not re-derived.\",\n );\n for (const attestation of v.attestations ?? []) {\n console.log(\n ` Attested by ${attestation.service} at ${attestation.commit.slice(0, 12)} (${attestation.recorded}).`,\n );\n if (attestation.report !== undefined) console.log(` from ${reportLine(attestation.report)}`);\n if (attestation.contractReport !== undefined) {\n console.log(` from ${contractReportLine(attestation.contractReport)}`);\n }\n }\n if (v.report !== undefined) console.log(` Answered from ${reportLine(v.report)}.`);\n if (v.contractReport !== undefined) console.log(` Contract report read: ${contractReportLine(v.contractReport)}.`);\n for (const notice of notices) console.log(` ⚠ ${notice.code}: ${notice.message}`);\n}\n\nexport const MARK: Record<string, string> = { confirmed: \"✓\", unconfirmed: \"✗\", unanswered: \"?\" };\n\n/**\n * The `recorded` block both views emit: where the record is, when it was\n * written, which checklist it answered, and the evidence provenance it carries.\n * `extra` lands right after `checklist` — the read view's `stale` — so each\n * payload keeps the key order it has always had.\n */\nexport function recordedPayload(\n target: VerifyTarget,\n v: Verification,\n extra: { stale?: boolean } = {},\n): Record<string, unknown> {\n return {\n path: repoPath(target.docsDir, recordLocation(target.featureDir)),\n recorded: v.recorded,\n checklist: v.checklist,\n ...extra,\n ...(v.attestations === undefined ? {} : { attestations: v.attestations }),\n ...(v.report === undefined ? {} : { report: v.report }),\n ...(v.contractReport === undefined ? {} : { contractReport: v.contractReport }),\n };\n}\n\n/**\n * Who answered, where that changes what the verdict means. `[runner]` is a\n * digest-matched green run; `[contract]` is an operationId-matched green\n * contract-test run — mechanical too, and marked so the record's provenance is\n * readable per line, not only in `contractReport:`; `[attested]` is a scenario\n * claim confirmed on somebody's word — the same ✓, a weaker fact, and it must\n * be visible on the line itself and not only in the summary. The attested test\n * runs FIRST and uses `attestedClaims`' own predicate (a confirmed scenario\n * claim not answered by the runner), so the mark can never disagree with the\n * tally: a scenario claim carrying `answered_by: external-runner` — a hand\n * edit, or a future loam; the reader accepts any string on purpose — COUNTS as\n * attested, and printing `[contract]` two lines above a notice saying \"an\n * agent's word\" would be the mark calling the tally a liar. Everything else is\n * unmarked: an agent's answer about a service existing or an operation being\n * exposed is exactly what the checklist asks for.\n */\nexport function answeredMark(c: AnsweredClaim): string {\n if (c.answered_by === \"runner\") return \" [runner]\";\n if (c.kind === \"scenario.tested\") {\n return c.verdict === \"confirmed\" ? \" [attested]\" : \"\";\n }\n return c.answered_by === \"external-runner\" ? \" [contract]\" : \"\";\n}\n\n/** Everything a verify surface has to say about a set of answers beyond the counts. */\nexport function noticesFor(claims: readonly AnsweredClaim[], feature: string): VerifyNotice[] {\n const attested = attestedNotice(claims, feature);\n return attested === null ? [] : [attested];\n}\n\n/**\n * `verify.checklist-forked` — two services answered DIFFERENT versions of this\n * feature{@link ServiceAttestation}'s question set.\n *\n * The record carries one top-level `checklist` digest, so before attestations\n * carried their own the file could not represent this at all: a service that\n * attested last week against a delta since rewritten wrote the same digest as\n * one that attested this morning, and the record-level staleness check flagged\n * both or neither. It could never say WHICH answers went stale, which is the\n * only question a reader has.\n *\n * A warn, and only ever a warn: a fork is normal mid-rollout — services attest\n * as they finish, and a feature legitimately changes between the first and the\n * last. What it must not do is stay invisible. Attestations with no `checklist`\n * field are excluded rather than counted as a third version: they make no claim\n * about what they answered, and reading silence as disagreement would fire this\n * on every record written before the field existed.\n *\n * In a record kept as per-service files the fork also decides what counts:\n * the union counts only the files that answered its newest version\n * (`core/verify/read/union.ts` says why), so the services named here are the\n * ones whose answers are left out until they record again. In a one-file\n * record the last writer had already pruned their stale answers, and the rest\n * still count — which is why the sentence below says only what is true of\n * both.\n */\nexport function forkedChecklistNotices(\n recorded: Verification | null,\n current: string,\n): VerifyNotice[] {\n const stated = (recorded?.attestations ?? []).filter((a) => a.checklist !== undefined);\n // Measured against the CURRENT checklist, not pairwise between attestations.\n // The pairwise reading answers the wrong question: three services that all\n // attested against a since-rewritten delta agree with each other perfectly\n // and are all stale, which pairwise calls clean. What a reader needs is\n // WHICH services answered something other than what is being asked now.\n const behind = stated.filter((a) => a.checklist !== current);\n if (behind.length === 0) return [];\n const byVersion = [...new Set(behind.map((a) => a.checklist!))]\n .sort()\n .map((v) => `${v} (${behind.filter((a) => a.checklist === v).map((a) => a.service).sort().join(\", \")})`);\n return [\n {\n code: \"verify.checklist-forked\",\n severity: \"warn\",\n message:\n `${plural(behind.length, \"service\")} answered a different version of this feature's question set ` +\n `than the one it now asks (${current}): ${byVersion.join(\"; \")}. ` +\n \"The feature changed after they recorded, so their answers are not about the questions being asked now. \" +\n \"Re-record them from their own repositories (`loam verify <FEAT> --service <id> --record`) — \" +\n \"nothing gates on it, but until they do, the record does not answer what the feature asks.\",\n },\n ];\n}\n\n/**\n * The claims one report could not attribute: a scenario digest — and therefore\n * an `@loam-digest-…` tag — that more than one service claims.\n *\n * This is now a GUARD rather than a diagnosis of ordinary authoring. Until the\n * digest was salted by service, two services wording a scenario identically\n * really did share one tag, and this is what stopped one repository's green run\n * from confirming the other's claim. `scenarioBodyHash` folds the owning\n * service into the hash, so a claim's digest is computed from the same service\n * the claim is filed under and two subjects cannot share one short of a\n * truncated-sha256 collision. It stays because the invariant is worth asserting\n * where it is relied on: if this ever fires, a digest is answering for two\n * services and no report can say which, so nothing here may confirm.\n *\n * Derived from claims rather than from a report, so the read view raises it too\n * — the collision exists the moment the checklist does, and learning about it\n * from a `--results` run that already happened is learning too late.\n */\nexport function contestedNotices(claims: readonly Claim[]): VerifyNotice[] {\n const contested = contestedDigests(claims);\n if (contested.size === 0) return [];\n const shared = [...contested].map(([digest, services]) => `${digest} (${services.join(\", \")})`);\n return [\n {\n code: \"verify.digest-contested\",\n severity: \"warn\",\n message:\n `${plural(contested.size, \"scenario digest\")} claimed by more than one service: ${shared.join(\"; \")}. ` +\n \"The digest is salted by service, so this is a hash collision, not shared wording — but one report still \" +\n \"cannot say whose suite ran them, so a shared --results run leaves them unconfirmed. \" +\n \"Record each service's claims from its own repository with --service.\",\n claims: claims.filter((c) => c.digest !== undefined && contested.has(c.digest)).map((c) => c.id),\n },\n ];\n}\n\n/**\n * The digest-contested rule's twin on the contract axis: operationIds that\n * more than one service on this checklist exposes, which one report can\n * therefore never attribute (`contestedOperations`, the module that also\n * leaves those claims unconfirmed). Raised where the contract report is\n * consumed, so the record path says WHY the claims came back unconfirmed\n * instead of leaving two bare ✗ lines to be misread as failures.\n */\nexport function contestedOperationNotices(claims: readonly Claim[]): VerifyNotice[] {\n const contested = contestedOperations(claims);\n if (contested.size === 0) return [];\n const shared = [...contested].map(([operation, services]) => `${operation} (${services.join(\", \")})`);\n return [\n {\n code: \"verify.operation-contested\",\n severity: \"warn\",\n message:\n `${plural(contested.size, \"operationId\")} exposed by more than one service on this checklist: ${shared.join(\"; \")}. ` +\n \"A contract report entry names no service, so a shared --contract-results run cannot say whose suite exercised them and leaves them unconfirmed. \" +\n \"Record each service's claims from its own repository with --service.\",\n claims: claims.filter((c) => c.operation !== undefined && contested.has(c.operation)).map((c) => c.id),\n },\n ];\n}\n\n/** The report a `--results` run consumed, as one line a reviewer can check by hand. */\nexport function reportLine(r: ConsumedReport): string {\n return `${r.path} (sha256 ${r.digest.slice(0, 12)}…, ${plural(r.scenarios, \"tagged scenario\")}, written ${r.mtime})`;\n}\n\n/**\n * The contract report a `--contract-results` run consumed — the same line for\n * the same promise, counting operations where the cucumber one counts tagged\n * scenarios. A separate function rather than a parameter on `reportLine`\n * because the two pins are different shapes, and a merged formatter would need\n * exactly the switching flag that near-duplicates are kept apart to avoid.\n */\nexport function contractReportLine(r: ConsumedContractReport): string {\n return `${r.path} (sha256 ${r.digest.slice(0, 12)}…, ${plural(r.operations, \"operation\")}, written ${r.mtime})`;\n}\n"]}
|
|
@@ -13,31 +13,37 @@ import { emitJson, fail, repoPath } from "../../core/envelope/json.js";
|
|
|
13
13
|
import { today } from "../../core/provenance/stamp.js";
|
|
14
14
|
import { runnerAnswers } from "../../core/results.js";
|
|
15
15
|
import { checkAnswers } from "../../core/verify/answers.js";
|
|
16
|
-
import {
|
|
16
|
+
import { buildServiceRecord, buildVerification } from "../../core/verify/build.js";
|
|
17
17
|
import {} from "../../core/verify/checklist.js";
|
|
18
18
|
import { contractAnswers } from "../../core/verify/evidence/contract.js";
|
|
19
|
+
import {} from "../../core/verify/read/file.js";
|
|
20
|
+
import { assembleRecord } from "../../core/verify/read/union.js";
|
|
19
21
|
import { commitVerification } from "../../core/verify/store/commit.js";
|
|
20
|
-
import { tallyRecord,
|
|
22
|
+
import { tallyRecord, verificationVerdict, } from "../../core/verify/record.js";
|
|
23
|
+
import { recordLocation, serviceRecordPath, verificationPath } from "../../core/verify/location.js";
|
|
21
24
|
import { plural, sayDiscarded, sayRecovered } from "../policy/format.js";
|
|
22
25
|
import { readContractResults } from "./evidence/contract.js";
|
|
23
26
|
import { claimTokens, pinAnswers } from "./evidence/pins.js";
|
|
24
|
-
import { readResults, repositoryCommit, validateServiceEvidence } from "./results.js";
|
|
25
|
-
import { contestedNotices, contestedOperationNotices, contractReportLine, noticesFor, reportLine } from "./frozen.js";
|
|
27
|
+
import { readResults, repositoryCommit, validateLocalEvidence, validateServiceEvidence } from "./results.js";
|
|
28
|
+
import { contestedNotices, contestedOperationNotices, contractReportLine, forkedChecklistNotices, noticesFor, reportLine, } from "./frozen.js";
|
|
26
29
|
import {} from "./report.js";
|
|
27
30
|
export async function record(target, checklist, attestor, opts) {
|
|
28
31
|
const { docsDir, featureDir, json } = target;
|
|
29
32
|
const { service, repoDir, previous } = attestor;
|
|
30
33
|
// The legacy all-at-once form answers the WHOLE checklist on one repository's
|
|
31
34
|
// word and writes a schema-1 record — no attestations, no commits. Run over a
|
|
32
|
-
// federated record it does not merge and it does not migrate:
|
|
33
|
-
// other service's commit-bound
|
|
34
|
-
//
|
|
35
|
-
//
|
|
35
|
+
// federated record it does not merge and it does not migrate: over a one-file
|
|
36
|
+
// federated record it would erase every other service's commit-bound
|
|
37
|
+
// attestation, invisibly, because what replaces it is a well-formed,
|
|
38
|
+
// plausible record; beside a `verification/` directory it would write a file
|
|
39
|
+
// whose answers can never count (no attestation binds them) while reporting
|
|
40
|
+
// them as recorded. Either way it refuses, and names the repositories whose
|
|
41
|
+
// word is on the record.
|
|
36
42
|
const attested = previous?.verification.schema === 2 ? (previous.verification.attestations ?? []) : [];
|
|
37
43
|
if (service === undefined && attested.length > 0) {
|
|
38
44
|
const services = [...new Set(attested.map((a) => a.service))].sort();
|
|
39
|
-
return fail(json, "record-federated", `${repoPath(docsDir,
|
|
40
|
-
"Recording without --service would
|
|
45
|
+
return fail(json, "record-federated", `${repoPath(docsDir, recordLocation(featureDir))} is a federated record: ${plural(services.length, "service")} (${services.join(", ")}) have attested claims against their own git commits. ` +
|
|
46
|
+
"Recording without --service would put one unattested answer set over all of it. " +
|
|
41
47
|
`Record this service's claims from its own repo instead: \`loam verify ${checklist.feature} --record answers.json --service <svc>\`.`);
|
|
42
48
|
}
|
|
43
49
|
const scopedClaims = service === undefined ? checklist.claims : checklist.claims.filter((claim) => claim.subject === service);
|
|
@@ -122,11 +128,36 @@ export async function record(target, checklist, attestor, opts) {
|
|
|
122
128
|
return fail(json, "answers-unevidenced", evidence.message);
|
|
123
129
|
({ answers, notice: pinNotice } = pinAnswers(answers, evidence, serviceCommit));
|
|
124
130
|
}
|
|
131
|
+
else {
|
|
132
|
+
// The all-at-once form binds to no commit, but a citation still has to be
|
|
133
|
+
// a line that exists — see `validateLocalEvidence`. Resolved against the
|
|
134
|
+
// repository loam.json was found in, which is the one place a reviewer
|
|
135
|
+
// reading this record can go and look.
|
|
136
|
+
const evidence = await validateLocalEvidence(answers, repoDir);
|
|
137
|
+
if (!evidence.ok) {
|
|
138
|
+
return fail(json, "answers-unevidenced", `${evidence.message} The all-at-once form resolves every citation inside this repository; ` +
|
|
139
|
+
"evidence that lives in a service's own repository is recorded from there, with `--service`.");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
125
142
|
const recorded = today(new Date());
|
|
143
|
+
// What goes on disk and what the record reads as afterwards are two
|
|
144
|
+
// different objects now. The all-at-once form IS its file. A service's write
|
|
145
|
+
// is one file among several, so the verdict it prints is the union of the
|
|
146
|
+
// files as they will stand once the commit lands — added up by the very
|
|
147
|
+
// function the next read will use, so this run cannot print a verdict the
|
|
148
|
+
// following `loam verify` disagrees with.
|
|
126
149
|
let verification;
|
|
150
|
+
let writes;
|
|
127
151
|
let discarded = [];
|
|
128
152
|
if (service === undefined) {
|
|
129
153
|
verification = buildVerification(checklist, answers, recorded, reports);
|
|
154
|
+
// The legacy all-at-once form keeps the one file, deliberately. It answers
|
|
155
|
+
// the whole checklist from one place on one word, with no commit and no
|
|
156
|
+
// service, so there is no second writer for it to collide with and no
|
|
157
|
+
// service to name a file after — and every record it ever wrote, the
|
|
158
|
+
// example fleet's included, stays byte-for-byte what it is. The moment a
|
|
159
|
+
// service attests, the refusal above keeps it off the record for good.
|
|
160
|
+
writes = [{ path: verificationPath(featureDir), record: verification, preImage: previous?.files.legacy?.raw ?? null }];
|
|
130
161
|
}
|
|
131
162
|
else {
|
|
132
163
|
// The docs side of the pin, and it is ASKED FOR rather than required: a
|
|
@@ -134,22 +165,39 @@ export async function record(target, checklist, attestor, opts) {
|
|
|
134
165
|
// carries no `docsCommit` when git cannot answer. Failing the record over
|
|
135
166
|
// it would refuse a working fleet for a field that is optional by design.
|
|
136
167
|
const docsHead = await repositoryCommit(docsDir);
|
|
137
|
-
const
|
|
168
|
+
const files = previous?.files ?? { legacy: null, services: new Map() };
|
|
169
|
+
const own = files.services.get(service) ?? null;
|
|
170
|
+
const built = buildServiceRecord(checklist, {
|
|
138
171
|
service,
|
|
139
172
|
recorded,
|
|
140
173
|
commit: serviceCommit,
|
|
141
174
|
reports,
|
|
142
175
|
...(docsHead.ok ? { docsCommit: docsHead.commit } : {}),
|
|
143
|
-
}, answers,
|
|
144
|
-
verification = built.verification;
|
|
176
|
+
}, answers, { legacy: files.legacy?.verification ?? null, own: own?.verification ?? null, filed: new Set(files.services.keys()) });
|
|
145
177
|
discarded = built.discarded;
|
|
178
|
+
// This service's own file FIRST — see `commitVerification` for why the
|
|
179
|
+
// order is what a kill between the two renames leaves standing.
|
|
180
|
+
writes = [
|
|
181
|
+
{ path: serviceRecordPath(featureDir, service), record: built.record, preImage: own?.raw ?? null },
|
|
182
|
+
...(built.dropLegacy && files.legacy !== null
|
|
183
|
+
? [{ path: files.legacy.path, record: null, preImage: files.legacy.raw }]
|
|
184
|
+
: []),
|
|
185
|
+
];
|
|
186
|
+
const after = new Map([...files.services].map(([svc, file]) => [svc, file.verification]));
|
|
187
|
+
after.set(service, built.record);
|
|
188
|
+
// Added up against THIS checklist — the one the answers just written
|
|
189
|
+
// answer. Left to pick by date, a same-day file with a later service id
|
|
190
|
+
// that answered an older checklist won the tie, and this run printed a
|
|
191
|
+
// tally its own answers were not in (`core/verify/read/union.ts`).
|
|
192
|
+
verification = assembleRecord(built.dropLegacy ? null : (files.legacy?.verification ?? null), [...after.keys()].sort().map((svc) => after.get(svc)), checklist.digest);
|
|
146
193
|
}
|
|
147
194
|
// Staged, compared against the locked read's bytes, swapped in by one
|
|
148
|
-
// rename — see core/verify/store/commit.ts for what each refusal
|
|
149
|
-
|
|
195
|
+
// rename per file — see core/verify/store/commit.ts for what each refusal
|
|
196
|
+
// means.
|
|
197
|
+
const committed = await commitVerification(writes, docsDir);
|
|
150
198
|
if (!committed.ok)
|
|
151
199
|
return fail(json, committed.code, committed.message);
|
|
152
|
-
const path =
|
|
200
|
+
const path = writes[0].path;
|
|
153
201
|
const unconfirmed = verification.claims.filter((c) => c.verdict === "unconfirmed");
|
|
154
202
|
// The same judgment read mode makes (see report): the record just written IS
|
|
155
203
|
// the current checklist's answer, so `stale` is false by construction and the
|
|
@@ -162,6 +210,10 @@ export async function record(target, checklist, attestor, opts) {
|
|
|
162
210
|
const verified = verdict === "verified";
|
|
163
211
|
const notices = [
|
|
164
212
|
...noticesFor(verification.claims, verification.feature),
|
|
213
|
+
// The read view's notice, on the write that makes it true: a service
|
|
214
|
+
// whose file answered another version of the checklist is named here,
|
|
215
|
+
// because its answers are exactly the ones this tally leaves out.
|
|
216
|
+
...forkedChecklistNotices(verification, checklist.digest),
|
|
165
217
|
...contestedNotices(runnerClaims),
|
|
166
218
|
...contestedOperationNotices(contractClaims),
|
|
167
219
|
...(pinNotice === null ? [] : [pinNotice]),
|
|
@@ -211,7 +263,7 @@ export async function record(target, checklist, attestor, opts) {
|
|
|
211
263
|
for (const c of unconfirmed) {
|
|
212
264
|
console.log(` ✗ ${c.claim}${c.note === undefined ? "" : ` — ${c.note}`}`);
|
|
213
265
|
}
|
|
214
|
-
sayDiscarded(discarded, repoPath(docsDir,
|
|
266
|
+
sayDiscarded(discarded, repoPath(docsDir, recordLocation(featureDir)), verification.feature);
|
|
215
267
|
console.log("");
|
|
216
268
|
for (const notice of notices)
|
|
217
269
|
console.log(` ⚠ ${notice.code}: ${notice.message}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../../src/commands/verify/record.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Writing the answers down.\n *\n * The one command in loam that takes somebody's word for something, so every\n * refusal here is about whose word is on the file. The legacy all-at-once form\n * run over a federated record does not merge and does not migrate — it would\n * erase every other service's commit-bound attestation, invisibly, because what\n * replaces it is a well-formed and plausible record.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { emitJson, fail, repoPath } from \"../../core/envelope/json.js\";\nimport { today } from \"../../core/provenance/stamp.js\";\nimport { runnerAnswers } from \"../../core/results.js\";\nimport { checkAnswers, type Answer } from \"../../core/verify/answers.js\";\nimport {\n buildFederatedVerification,\n buildVerification,\n type DiscardedAnswer,\n} from \"../../core/verify/build.js\";\nimport { type Checklist } from \"../../core/verify/checklist.js\";\nimport { contractAnswers } from \"../../core/verify/evidence/contract.js\";\nimport { commitVerification } from \"../../core/verify/store/commit.js\";\nimport {\n tallyRecord,\n type ConsumedReports,\n type Verification,\n type VerifyNotice,\n verificationPath,\n verificationVerdict,\n} from \"../../core/verify/record.js\";\nimport { plural, sayDiscarded, sayRecovered } from \"../policy/format.js\";\nimport { readContractResults } from \"./evidence/contract.js\";\nimport { claimTokens, pinAnswers } from \"./evidence/pins.js\";\nimport { readResults, repositoryCommit, validateServiceEvidence } from \"./results.js\";\nimport { contestedNotices, contestedOperationNotices, contractReportLine, noticesFor, reportLine } from \"./frozen.js\";\nimport { type VerifyTarget } from \"./report.js\";\n\n/** The flags `loam verify --record` reads. */\nexport interface VerifyOptions {\n record?: string;\n results?: string;\n contractResults?: string;\n /** `--diff-answers a.json b.json` — the read-only cross-examination lens\n * (./cross/diff.ts): both files validate against the current checklist and\n * nothing is written. verify.ts refuses recording combinations and arity ≠ 2. */\n diffAnswers?: string[];\n service?: string;\n json?: boolean;\n}\n\n/**\n * The repository whose word this record is taking, for which service, and over\n * which existing record.\n *\n * `previous` belongs here rather than beside it: every refusal in this function\n * is about whether THIS attestor may write over what that record already says,\n * so the two are never asked about separately.\n */\nexport interface Attestor {\n /** Undefined in the legacy all-at-once form, which answers for every service. */\n service: string | undefined;\n repoDir: string;\n /**\n * The existing record as ONE read — the parse the merge consumes and the\n * exact bytes it was parsed from — or null when there was no record. One\n * field rather than a `Verification` beside a `Buffer` because the two are\n * meaningless apart: a pre-image from any other read would let the commit\n * vouch for a document the merge never saw, and as separate fields that\n * inconsistent pair is representable and compiles.\n */\n previous: { verification: Verification; raw: Buffer } | null;\n}\n\nexport async function record(\n target: VerifyTarget,\n checklist: Checklist,\n attestor: Attestor,\n opts: VerifyOptions,\n): Promise<void> {\n const { docsDir, featureDir, json } = target;\n const { service, repoDir, previous } = attestor;\n // The legacy all-at-once form answers the WHOLE checklist on one repository's\n // word and writes a schema-1 record — no attestations, no commits. Run over a\n // federated record it does not merge and it does not migrate: it erases every\n // other service's commit-bound attestation, and the erasure is invisible\n // afterwards because what replaces it is a well-formed, plausible record. So\n // it refuses, and names the repositories whose word is on the file.\n const attested = previous?.verification.schema === 2 ? (previous.verification.attestations ?? []) : [];\n if (service === undefined && attested.length > 0) {\n const services = [...new Set(attested.map((a) => a.service))].sort();\n return fail(\n json,\n \"record-federated\",\n `${repoPath(docsDir, verificationPath(featureDir))} is a federated record: ${plural(services.length, \"service\")} (${services.join(\", \")}) have attested claims against their own git commits. ` +\n \"Recording without --service would replace all of it with one unattested answer set. \" +\n `Record this service's claims from its own repo instead: \\`loam verify ${checklist.feature} --record answers.json --service <svc>\\`.`,\n );\n }\n\n const scopedClaims =\n service === undefined ? checklist.claims : checklist.claims.filter((claim) => claim.subject === service);\n if (service !== undefined && scopedClaims.length === 0) {\n return fail(\n json,\n \"unknown-service\",\n `The current ${checklist.feature} checklist has no claims owned by service '${service}'.`,\n );\n }\n\n // The mechanical halves: with --results, every scenario.tested claim is the\n // cucumber report's to answer — matched by digest, confirmed only by a green\n // run — and with --contract-results, every api.exposes claim is the contract\n // report's, matched by operationId under the same ownership discipline. A\n // partial suite leaves its unexercised claims unconfirmed rather than\n // agent-attestable in the same run: silence must not read as checked, and\n // the escape is a --record run without the flag.\n const runnerClaims =\n opts.results === undefined ? [] : scopedClaims.filter((c) => c.kind === \"scenario.tested\");\n const contractClaims =\n opts.contractResults === undefined ? [] : scopedClaims.filter((c) => c.kind === \"api.exposes\");\n const agentClaims = scopedClaims.filter(\n (c) =>\n (opts.results === undefined || c.kind !== \"scenario.tested\") &&\n (opts.contractResults === undefined || c.kind !== \"api.exposes\"),\n );\n\n let fromRunner: Answer[] = [];\n let fromContract: Answer[] = [];\n const reports: ConsumedReports = {};\n if (opts.results !== undefined) {\n // A federated record attests for THIS repository, so its report has to be a\n // file inside it — see evidence/read.ts.\n const read = await readResults(opts.results, service === undefined ? undefined : repoDir);\n if (!read.ok) return fail(json, read.code, read.message);\n reports.results = read.report;\n fromRunner = runnerAnswers(runnerClaims, read.scenarios, read.report.path);\n }\n if (opts.contractResults !== undefined) {\n // Same resolution rules as the cucumber report, same refusal code: the two\n // pins make the same promise about the same repository.\n const read = await readContractResults(opts.contractResults, service === undefined ? undefined : repoDir);\n if (!read.ok) return fail(json, read.code, read.message);\n reports.contract = read.report;\n fromContract = contractAnswers(contractClaims, read.runs, read.report.path);\n }\n\n // The agent's half — exactly what no report owns. A mechanical flag alone is\n // legal only when the reports own the whole checklist: anything left over\n // refuses with the ids, the same discipline as a claim with no answer.\n let raw: unknown = [];\n if (opts.record !== undefined) {\n try {\n // The one path that stays cwd-relative, and deliberately: the answer set\n // is a file the caller just wrote and hands to the command, not part of\n // the repository being attested. Nothing on the record points at it.\n raw = JSON.parse(await readFile(resolve(process.cwd(), opts.record), \"utf8\"));\n } catch (err) {\n return fail(\n json,\n \"answers-unreadable\",\n `Cannot read ${opts.record}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n } else if (agentClaims.length > 0 && (opts.results !== undefined || opts.contractResults !== undefined)) {\n const owns = [\n ...(opts.results === undefined ? [] : [\"--results answers only the scenario.tested claims\"]),\n ...(opts.contractResults === undefined ? [] : [\"--contract-results answers only the api.exposes claims\"]),\n ].join(\" and \");\n return fail(\n json,\n \"answers-mismatch\",\n `${owns}; ${agentClaims.length} claim(s) have no answer: ` +\n `${agentClaims.map((c) => c.id).join(\", \")}. Record them with --record <answers.json> alongside.`,\n );\n }\n\n const checked = checkAnswers(\n agentClaims,\n raw,\n opts.results === undefined && opts.contractResults === undefined\n ? undefined\n : new Set([...runnerClaims, ...contractClaims].map((c) => c.id)),\n { feature: checklist.feature, ...(service === undefined ? {} : { service }) },\n );\n if (!checked.ok) return fail(json, checked.code, checked.message);\n\n let serviceCommit: string | undefined;\n let answers = [...fromRunner, ...fromContract, ...checked.answers];\n let pinNotice: VerifyNotice | null = null;\n if (service !== undefined) {\n const commit = await repositoryCommit(repoDir);\n if (!commit.ok) return fail(json, \"repository-unavailable\", commit.message);\n serviceCommit = commit.commit;\n // All halves, one validator. The runner's evidence used to skip it\n // entirely: `--results` minted confirmations whose evidence strings nothing\n // ever checked, in the one mode that promises every answer is bound to this\n // repository at this commit. The same pass stamps each agent citation's\n // evidence pin and scans its blob for the claim's token (evidence/pins.ts).\n const evidence = await validateServiceEvidence(answers, { repoDir, commit: serviceCommit }, reports, claimTokens(scopedClaims));\n if (!evidence.ok) return fail(json, \"answers-unevidenced\", evidence.message);\n ({ answers, notice: pinNotice } = pinAnswers(answers, evidence, serviceCommit));\n }\n\n const recorded = today(new Date());\n let verification: Verification;\n let discarded: DiscardedAnswer[] = [];\n if (service === undefined) {\n verification = buildVerification(checklist, answers, recorded, reports);\n } else {\n // The docs side of the pin, and it is ASKED FOR rather than required: a\n // docs repo is not obliged to be a git checkout, and the attestation simply\n // carries no `docsCommit` when git cannot answer. Failing the record over\n // it would refuse a working fleet for a field that is optional by design.\n const docsHead = await repositoryCommit(docsDir);\n const built = buildFederatedVerification(\n checklist,\n {\n service,\n recorded,\n commit: serviceCommit!,\n reports,\n ...(docsHead.ok ? { docsCommit: docsHead.commit } : {}),\n },\n answers,\n previous?.verification ?? null,\n );\n verification = built.verification;\n discarded = built.discarded;\n }\n // Staged, compared against the locked read's bytes, swapped in by one\n // rename — see core/verify/store/commit.ts for what each refusal means.\n const committed = await commitVerification(featureDir, verification, previous?.raw ?? null, docsDir);\n if (!committed.ok) return fail(json, committed.code, committed.message);\n const path = committed.path;\n const unconfirmed = verification.claims.filter((c) => c.verdict === \"unconfirmed\");\n\n // The same judgment read mode makes (see report): the record just written IS\n // the current checklist's answer, so `stale` is false by construction and the\n // remaining question is whether every claim was confirmed — and, now, by\n // whom. Counted from the claims, not from the summary this same call wrote.\n // Without these fields an agent that recorded all-confirmed answers would\n // have to re-run verify just to learn the state it created.\n const tally = tallyRecord(verification);\n const verdict = verificationVerdict(tally);\n const verified = verdict === \"verified\";\n const notices = [\n ...noticesFor(verification.claims, verification.feature),\n ...contestedNotices(runnerClaims),\n ...contestedOperationNotices(contractClaims),\n ...(pinNotice === null ? [] : [pinNotice]),\n ];\n\n if (json) {\n emitJson({\n command: \"verify\",\n feature: verification.feature,\n path: repoPath(docsDir, path),\n ...(target.recovered == null ? {} : { recovered: target.recovered }),\n digest: verification.checklist,\n verified,\n verdict,\n attested: tally.attested,\n recorded: verification.recorded,\n summary: verification.summary,\n ...(verification.attestations === undefined ? {} : { attestations: verification.attestations }),\n ...(reports.results === undefined ? {} : { report: reports.results }),\n ...(reports.contract === undefined ? {} : { contractReport: reports.contract }),\n unconfirmed: unconfirmed.map((c) => ({ id: c.id, claim: c.claim, ...(c.note === undefined ? {} : { note: c.note }) })),\n ...(discarded.length === 0 ? {} : { discarded }),\n ...(notices.length === 0 ? {} : { notices }),\n });\n return;\n }\n\n if (target.recovered != null) console.log(`${sayRecovered(target.recovered)}\\n`);\n console.log(`${verification.feature} verification recorded — ${repoPath(docsDir, path)}\\n`);\n console.log(` ${tally.confirmed} of ${plural(tally.claims, \"claim\")} confirmed with evidence.`);\n if (reports.results !== undefined) {\n console.log(` ${plural(fromRunner.length, \"scenario claim\")} answered by the test runner (${opts.results}).`);\n console.log(` Report read: ${reportLine(reports.results)}.`);\n }\n if (reports.contract !== undefined) {\n console.log(` ${plural(fromContract.length, \"api.exposes claim\")} answered by the contract test run (${opts.contractResults}).`);\n console.log(` Contract report read: ${contractReportLine(reports.contract)}.`);\n }\n if (opts.record !== undefined && (reports.results !== undefined || reports.contract !== undefined)) {\n console.log(` ${plural(checked.answers.length, \"claim\")} answered by ${opts.record}.`);\n }\n if (service !== undefined) {\n const attestation = verification.attestations?.find((item) => item.service === service);\n if (attestation !== undefined) {\n console.log(` ${service} attested at git commit ${attestation.commit}.`);\n }\n }\n for (const c of unconfirmed) {\n console.log(` ✗ ${c.claim}${c.note === undefined ? \"\" : ` — ${c.note}`}`);\n }\n\n sayDiscarded(discarded, repoPath(docsDir, verificationPath(featureDir)), verification.feature);\n console.log(\"\");\n for (const notice of notices) console.log(` ⚠ ${notice.code}: ${notice.message}`);\n console.log(\n tally.unanswered > 0\n ? ` Partial federation — ${plural(tally.unanswered, \"claim\")} remain unanswered for their owning service repositories.`\n : verdict === \"attested\"\n ? \" Recorded as attested, not verified: the record travels into features/archive/ saying so.\"\n : unconfirmed.length === 0\n ? \" The record travels with the feature into features/archive/.\"\n : \" Recorded as it stands. Nothing gates on this — it is what a reviewer reads later, so leave it true.\",\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../../src/commands/verify/record.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Writing the answers down.\n *\n * The one command in loam that takes somebody's word for something, so every\n * refusal here is about whose word is on the file. The legacy all-at-once form\n * run over a federated record does not merge and does not migrate — it would\n * erase every other service's commit-bound attestation, invisibly, because what\n * replaces it is a well-formed and plausible record.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { emitJson, fail, repoPath } from \"../../core/envelope/json.js\";\nimport { today } from \"../../core/provenance/stamp.js\";\nimport { runnerAnswers } from \"../../core/results.js\";\nimport { checkAnswers, type Answer } from \"../../core/verify/answers.js\";\nimport { buildServiceRecord, buildVerification, type DiscardedAnswer } from \"../../core/verify/build.js\";\nimport { type Checklist } from \"../../core/verify/checklist.js\";\nimport { contractAnswers } from \"../../core/verify/evidence/contract.js\";\nimport { type RecordFiles } from \"../../core/verify/read/file.js\";\nimport { assembleRecord } from \"../../core/verify/read/union.js\";\nimport { commitVerification, type RecordWrite } from \"../../core/verify/store/commit.js\";\nimport {\n tallyRecord,\n type ConsumedReports,\n type Verification,\n type VerifyNotice,\n verificationVerdict,\n} from \"../../core/verify/record.js\";\nimport { recordLocation, serviceRecordPath, verificationPath } from \"../../core/verify/location.js\";\nimport type { PathableService } from \"../../core/kernel/ids/service.js\";\nimport { plural, sayDiscarded, sayRecovered } from \"../policy/format.js\";\nimport { readContractResults } from \"./evidence/contract.js\";\nimport { claimTokens, pinAnswers } from \"./evidence/pins.js\";\nimport { readResults, repositoryCommit, validateLocalEvidence, validateServiceEvidence } from \"./results.js\";\nimport {\n contestedNotices,\n contestedOperationNotices,\n contractReportLine,\n forkedChecklistNotices,\n noticesFor,\n reportLine,\n} from \"./frozen.js\";\nimport { type VerifyTarget } from \"./report.js\";\n\n/** The flags `loam verify --record` reads. */\nexport interface VerifyOptions {\n record?: string;\n results?: string;\n contractResults?: string;\n /** `--diff-answers a.json b.json` — the read-only cross-examination lens\n * (./cross/diff.ts): both files validate against the current checklist and\n * nothing is written. verify.ts refuses recording combinations and arity ≠ 2. */\n diffAnswers?: string[];\n service?: string;\n json?: boolean;\n}\n\n/**\n * The repository whose word this record is taking, for which service, and over\n * which existing record.\n *\n * `previous` belongs here rather than beside it: every refusal in this function\n * is about whether THIS attestor may write over what that record already says,\n * so the two are never asked about separately.\n */\nexport interface Attestor {\n /**\n * Undefined in the legacy all-at-once form, which answers for every service.\n * Branded because it becomes a file name (`verification/<svc>.yaml`): the\n * caller hands in loam.json's own validated id, never the raw flag.\n */\n service: PathableService | undefined;\n /**\n * The repository an attestation is ABOUT: the commit it names, the tree its\n * evidence must resolve inside, and where a `--results` report has to live.\n * It is threaded rather than read in `record` because it comes from\n * `config.root`, and a `process.cwd()` in that function was the whole bug —\n * from a subdirectory, \"this repo\" quietly became that subdirectory.\n */\n repoDir: string;\n /**\n * The existing record as ONE read — the union the refusals consult and the\n * files it was added up from, each with the exact bytes it was parsed from —\n * or null when there was no record. One field rather than a `Verification`\n * beside a set of `Buffer`s because they are meaningless apart: a pre-image\n * from any other read would let the commit vouch for a document the merge\n * never saw, and as separate fields that inconsistent pair is representable\n * and compiles.\n */\n previous: { verification: Verification; files: RecordFiles } | null;\n}\n\nexport async function record(\n target: VerifyTarget,\n checklist: Checklist,\n attestor: Attestor,\n opts: VerifyOptions,\n): Promise<void> {\n const { docsDir, featureDir, json } = target;\n const { service, repoDir, previous } = attestor;\n // The legacy all-at-once form answers the WHOLE checklist on one repository's\n // word and writes a schema-1 record — no attestations, no commits. Run over a\n // federated record it does not merge and it does not migrate: over a one-file\n // federated record it would erase every other service's commit-bound\n // attestation, invisibly, because what replaces it is a well-formed,\n // plausible record; beside a `verification/` directory it would write a file\n // whose answers can never count (no attestation binds them) while reporting\n // them as recorded. Either way it refuses, and names the repositories whose\n // word is on the record.\n const attested = previous?.verification.schema === 2 ? (previous.verification.attestations ?? []) : [];\n if (service === undefined && attested.length > 0) {\n const services = [...new Set(attested.map((a) => a.service))].sort();\n return fail(\n json,\n \"record-federated\",\n `${repoPath(docsDir, recordLocation(featureDir))} is a federated record: ${plural(services.length, \"service\")} (${services.join(\", \")}) have attested claims against their own git commits. ` +\n \"Recording without --service would put one unattested answer set over all of it. \" +\n `Record this service's claims from its own repo instead: \\`loam verify ${checklist.feature} --record answers.json --service <svc>\\`.`,\n );\n }\n\n const scopedClaims =\n service === undefined ? checklist.claims : checklist.claims.filter((claim) => claim.subject === service);\n if (service !== undefined && scopedClaims.length === 0) {\n return fail(\n json,\n \"unknown-service\",\n `The current ${checklist.feature} checklist has no claims owned by service '${service}'.`,\n );\n }\n\n // The mechanical halves: with --results, every scenario.tested claim is the\n // cucumber report's to answer — matched by digest, confirmed only by a green\n // run — and with --contract-results, every api.exposes claim is the contract\n // report's, matched by operationId under the same ownership discipline. A\n // partial suite leaves its unexercised claims unconfirmed rather than\n // agent-attestable in the same run: silence must not read as checked, and\n // the escape is a --record run without the flag.\n const runnerClaims =\n opts.results === undefined ? [] : scopedClaims.filter((c) => c.kind === \"scenario.tested\");\n const contractClaims =\n opts.contractResults === undefined ? [] : scopedClaims.filter((c) => c.kind === \"api.exposes\");\n const agentClaims = scopedClaims.filter(\n (c) =>\n (opts.results === undefined || c.kind !== \"scenario.tested\") &&\n (opts.contractResults === undefined || c.kind !== \"api.exposes\"),\n );\n\n let fromRunner: Answer[] = [];\n let fromContract: Answer[] = [];\n const reports: ConsumedReports = {};\n if (opts.results !== undefined) {\n // A federated record attests for THIS repository, so its report has to be a\n // file inside it — see evidence/read.ts.\n const read = await readResults(opts.results, service === undefined ? undefined : repoDir);\n if (!read.ok) return fail(json, read.code, read.message);\n reports.results = read.report;\n fromRunner = runnerAnswers(runnerClaims, read.scenarios, read.report.path);\n }\n if (opts.contractResults !== undefined) {\n // Same resolution rules as the cucumber report, same refusal code: the two\n // pins make the same promise about the same repository.\n const read = await readContractResults(opts.contractResults, service === undefined ? undefined : repoDir);\n if (!read.ok) return fail(json, read.code, read.message);\n reports.contract = read.report;\n fromContract = contractAnswers(contractClaims, read.runs, read.report.path);\n }\n\n // The agent's half — exactly what no report owns. A mechanical flag alone is\n // legal only when the reports own the whole checklist: anything left over\n // refuses with the ids, the same discipline as a claim with no answer.\n let raw: unknown = [];\n if (opts.record !== undefined) {\n try {\n // The one path that stays cwd-relative, and deliberately: the answer set\n // is a file the caller just wrote and hands to the command, not part of\n // the repository being attested. Nothing on the record points at it.\n raw = JSON.parse(await readFile(resolve(process.cwd(), opts.record), \"utf8\"));\n } catch (err) {\n return fail(\n json,\n \"answers-unreadable\",\n `Cannot read ${opts.record}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n } else if (agentClaims.length > 0 && (opts.results !== undefined || opts.contractResults !== undefined)) {\n const owns = [\n ...(opts.results === undefined ? [] : [\"--results answers only the scenario.tested claims\"]),\n ...(opts.contractResults === undefined ? [] : [\"--contract-results answers only the api.exposes claims\"]),\n ].join(\" and \");\n return fail(\n json,\n \"answers-mismatch\",\n `${owns}; ${agentClaims.length} claim(s) have no answer: ` +\n `${agentClaims.map((c) => c.id).join(\", \")}. Record them with --record <answers.json> alongside.`,\n );\n }\n\n const checked = checkAnswers(\n agentClaims,\n raw,\n opts.results === undefined && opts.contractResults === undefined\n ? undefined\n : new Set([...runnerClaims, ...contractClaims].map((c) => c.id)),\n { feature: checklist.feature, ...(service === undefined ? {} : { service }) },\n );\n if (!checked.ok) return fail(json, checked.code, checked.message);\n\n let serviceCommit: string | undefined;\n let answers = [...fromRunner, ...fromContract, ...checked.answers];\n let pinNotice: VerifyNotice | null = null;\n if (service !== undefined) {\n const commit = await repositoryCommit(repoDir);\n if (!commit.ok) return fail(json, \"repository-unavailable\", commit.message);\n serviceCommit = commit.commit;\n // All halves, one validator. The runner's evidence used to skip it\n // entirely: `--results` minted confirmations whose evidence strings nothing\n // ever checked, in the one mode that promises every answer is bound to this\n // repository at this commit. The same pass stamps each agent citation's\n // evidence pin and scans its blob for the claim's token (evidence/pins.ts).\n const evidence = await validateServiceEvidence(answers, { repoDir, commit: serviceCommit }, reports, claimTokens(scopedClaims));\n if (!evidence.ok) return fail(json, \"answers-unevidenced\", evidence.message);\n ({ answers, notice: pinNotice } = pinAnswers(answers, evidence, serviceCommit));\n } else {\n // The all-at-once form binds to no commit, but a citation still has to be\n // a line that exists — see `validateLocalEvidence`. Resolved against the\n // repository loam.json was found in, which is the one place a reviewer\n // reading this record can go and look.\n const evidence = await validateLocalEvidence(answers, repoDir);\n if (!evidence.ok) {\n return fail(\n json,\n \"answers-unevidenced\",\n `${evidence.message} The all-at-once form resolves every citation inside this repository; ` +\n \"evidence that lives in a service's own repository is recorded from there, with `--service`.\",\n );\n }\n }\n\n const recorded = today(new Date());\n // What goes on disk and what the record reads as afterwards are two\n // different objects now. The all-at-once form IS its file. A service's write\n // is one file among several, so the verdict it prints is the union of the\n // files as they will stand once the commit lands — added up by the very\n // function the next read will use, so this run cannot print a verdict the\n // following `loam verify` disagrees with.\n let verification: Verification;\n let writes: RecordWrite[];\n let discarded: DiscardedAnswer[] = [];\n if (service === undefined) {\n verification = buildVerification(checklist, answers, recorded, reports);\n // The legacy all-at-once form keeps the one file, deliberately. It answers\n // the whole checklist from one place on one word, with no commit and no\n // service, so there is no second writer for it to collide with and no\n // service to name a file after — and every record it ever wrote, the\n // example fleet's included, stays byte-for-byte what it is. The moment a\n // service attests, the refusal above keeps it off the record for good.\n writes = [{ path: verificationPath(featureDir), record: verification, preImage: previous?.files.legacy?.raw ?? null }];\n } else {\n // The docs side of the pin, and it is ASKED FOR rather than required: a\n // docs repo is not obliged to be a git checkout, and the attestation simply\n // carries no `docsCommit` when git cannot answer. Failing the record over\n // it would refuse a working fleet for a field that is optional by design.\n const docsHead = await repositoryCommit(docsDir);\n const files = previous?.files ?? { legacy: null, services: new Map() };\n const own = files.services.get(service) ?? null;\n const built = buildServiceRecord(\n checklist,\n {\n service,\n recorded,\n commit: serviceCommit!,\n reports,\n ...(docsHead.ok ? { docsCommit: docsHead.commit } : {}),\n },\n answers,\n { legacy: files.legacy?.verification ?? null, own: own?.verification ?? null, filed: new Set(files.services.keys()) },\n );\n discarded = built.discarded;\n // This service's own file FIRST — see `commitVerification` for why the\n // order is what a kill between the two renames leaves standing.\n writes = [\n { path: serviceRecordPath(featureDir, service), record: built.record, preImage: own?.raw ?? null },\n ...(built.dropLegacy && files.legacy !== null\n ? [{ path: files.legacy.path, record: null, preImage: files.legacy.raw }]\n : []),\n ];\n const after = new Map([...files.services].map(([svc, file]) => [svc, file.verification]));\n after.set(service, built.record);\n // Added up against THIS checklist — the one the answers just written\n // answer. Left to pick by date, a same-day file with a later service id\n // that answered an older checklist won the tie, and this run printed a\n // tally its own answers were not in (`core/verify/read/union.ts`).\n verification = assembleRecord(\n built.dropLegacy ? null : (files.legacy?.verification ?? null),\n [...after.keys()].sort().map((svc) => after.get(svc)!),\n checklist.digest,\n )!;\n }\n // Staged, compared against the locked read's bytes, swapped in by one\n // rename per file — see core/verify/store/commit.ts for what each refusal\n // means.\n const committed = await commitVerification(writes, docsDir);\n if (!committed.ok) return fail(json, committed.code, committed.message);\n const path = writes[0]!.path;\n const unconfirmed = verification.claims.filter((c) => c.verdict === \"unconfirmed\");\n\n // The same judgment read mode makes (see report): the record just written IS\n // the current checklist's answer, so `stale` is false by construction and the\n // remaining question is whether every claim was confirmed — and, now, by\n // whom. Counted from the claims, not from the summary this same call wrote.\n // Without these fields an agent that recorded all-confirmed answers would\n // have to re-run verify just to learn the state it created.\n const tally = tallyRecord(verification);\n const verdict = verificationVerdict(tally);\n const verified = verdict === \"verified\";\n const notices = [\n ...noticesFor(verification.claims, verification.feature),\n // The read view's notice, on the write that makes it true: a service\n // whose file answered another version of the checklist is named here,\n // because its answers are exactly the ones this tally leaves out.\n ...forkedChecklistNotices(verification, checklist.digest),\n ...contestedNotices(runnerClaims),\n ...contestedOperationNotices(contractClaims),\n ...(pinNotice === null ? [] : [pinNotice]),\n ];\n\n if (json) {\n emitJson({\n command: \"verify\",\n feature: verification.feature,\n path: repoPath(docsDir, path),\n ...(target.recovered == null ? {} : { recovered: target.recovered }),\n digest: verification.checklist,\n verified,\n verdict,\n attested: tally.attested,\n recorded: verification.recorded,\n summary: verification.summary,\n ...(verification.attestations === undefined ? {} : { attestations: verification.attestations }),\n ...(reports.results === undefined ? {} : { report: reports.results }),\n ...(reports.contract === undefined ? {} : { contractReport: reports.contract }),\n unconfirmed: unconfirmed.map((c) => ({ id: c.id, claim: c.claim, ...(c.note === undefined ? {} : { note: c.note }) })),\n ...(discarded.length === 0 ? {} : { discarded }),\n ...(notices.length === 0 ? {} : { notices }),\n });\n return;\n }\n\n if (target.recovered != null) console.log(`${sayRecovered(target.recovered)}\\n`);\n console.log(`${verification.feature} verification recorded — ${repoPath(docsDir, path)}\\n`);\n console.log(` ${tally.confirmed} of ${plural(tally.claims, \"claim\")} confirmed with evidence.`);\n if (reports.results !== undefined) {\n console.log(` ${plural(fromRunner.length, \"scenario claim\")} answered by the test runner (${opts.results}).`);\n console.log(` Report read: ${reportLine(reports.results)}.`);\n }\n if (reports.contract !== undefined) {\n console.log(` ${plural(fromContract.length, \"api.exposes claim\")} answered by the contract test run (${opts.contractResults}).`);\n console.log(` Contract report read: ${contractReportLine(reports.contract)}.`);\n }\n if (opts.record !== undefined && (reports.results !== undefined || reports.contract !== undefined)) {\n console.log(` ${plural(checked.answers.length, \"claim\")} answered by ${opts.record}.`);\n }\n if (service !== undefined) {\n const attestation = verification.attestations?.find((item) => item.service === service);\n if (attestation !== undefined) {\n console.log(` ${service} attested at git commit ${attestation.commit}.`);\n }\n }\n for (const c of unconfirmed) {\n console.log(` ✗ ${c.claim}${c.note === undefined ? \"\" : ` — ${c.note}`}`);\n }\n\n sayDiscarded(discarded, repoPath(docsDir, recordLocation(featureDir)), verification.feature);\n console.log(\"\");\n for (const notice of notices) console.log(` ⚠ ${notice.code}: ${notice.message}`);\n console.log(\n tally.unanswered > 0\n ? ` Partial federation — ${plural(tally.unanswered, \"claim\")} remain unanswered for their owning service repositories.`\n : verdict === \"attested\"\n ? \" Recorded as attested, not verified: the record travels into features/archive/ saying so.\"\n : unconfirmed.length === 0\n ? \" The record travels with the feature into features/archive/.\"\n : \" Recorded as it stands. Nothing gates on this — it is what a reviewer reads later, so leave it true.\",\n );\n}\n"]}
|