@ybotok/loam 0.2.0-alpha.7 → 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 +1315 -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 +83 -7
- package/SCHEMA.md +569 -98
- 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 +33 -11
- 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 +21 -25
- package/dist/commands/seed/commit.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 +3 -5
- package/dist/commands/subsystem/edit.js.map +1 -1
- package/dist/commands/subsystem/move.js +5 -8
- 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 +14 -41
- package/dist/commands/subsystem/sync.js.map +1 -1
- package/dist/commands/subsystem/txn/txn.js +17 -38
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +32 -5
- 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/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/landscape.js +23 -23
- package/dist/commands/validate/fleet/landscape.js.map +1 -1
- package/dist/commands/validate/fleet/map/attest.js +6 -10
- package/dist/commands/validate/fleet/map/attest.js.map +1 -1
- package/dist/commands/validate/fleet/map/bindings.js +1 -3
- package/dist/commands/validate/fleet/map/bindings.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.map +1 -1
- package/dist/commands/validate/fleet/views/root-documents.js +8 -28
- package/dist/commands/validate/fleet/views/root-documents.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 +56 -101
- 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 +4 -3
- 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 +32 -18
- package/dist/core/agent/agents-md/refusals.js.map +1 -1
- package/dist/core/agent/agents-md.js +9 -3
- 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 +20 -16
- 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 +50 -18
- 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 +6 -18
- 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 +15 -5
- package/dist/core/brief/targets.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/documents.js +31 -6
- package/dist/core/c4/project/documents.js.map +1 -1
- package/dist/core/c4/project/load.js +6 -17
- package/dist/core/c4/project/load.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 +3 -6
- 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 +5 -2
- package/dist/core/gate/checks.js.map +1 -1
- package/dist/core/gate/partners.js +4 -2
- 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/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/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 +3 -0
- 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 +12 -2
- package/dist/core/vocabulary/maturity.js.map +1 -1
- package/examples/README.md +21 -6
- package/examples/docs/architecture/landscape.likec4 +8 -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/platform/notification-service/asyncapi.yaml +2 -1
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"next.js","sourceRoot":"","sources":["../../../../src/core/status/fleet/next.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `next[]` — the whole repository: adoption gaps first, then every feature in\n * flight, most-unblocking first, capped so the list stays navigation rather\n * than inventory.\n */\nimport type { DependencyGraph } from \"../../dependencies/facts.js\";\nimport { serviceTreePath } from \"../../kernel/ids/dirs.js\";\nimport { compareIds, type ServiceEntry } from \"../../repo/entries.js\";\nimport { recoverStep } from \"../interrupted.js\";\nimport {\n undocumented,\n type ArtifactStatus,\n type FleetFeatureState,\n type InterruptedCommit,\n type NextStep,\n} from \"../report.js\";\n\n/** How urgent a stage is at fleet level. See {@link fleetNext}. */\nconst STAGE_RANK: Record<ArtifactStatus, number> = {\n done: 0,\n ready: 1,\n draft: 2,\n missing: 3,\n blocked: 4,\n};\n\n/**\n * How many per-service and per-feature steps the fleet form will print before\n * it stops and says how many it left out. A fleet mid-adoption produced 135\n * entries under the heading \"the next thing to do\" — a list nobody reads is a\n * list that is not navigation, and the first ten are the ten that matter\n * because the ordering below is already most-unblocking-first.\n *\n * Exported so the test asserts the cap through the constant: a literal 10 in a\n * fixture is a second declaration of the limit, free to disagree the day this\n * one moves.\n */\nexport const FLEET_NEXT_LIMIT = 10;\n\n/**\n * Fleet-level steps, most-unblocking first. A feature ready to ship comes\n * before one that still needs authoring, because archiving it is what releases\n * everything queued behind it; a blocked feature comes last, because there is\n * nothing to do about it from here — its prerequisite is already higher up the\n * list under its own entry.\n *\n * The gate is the LAST entry and it is always there. `next.fleet-clean` used to\n * be the only route to `loam validate --all`, and it was emitted only when\n * nothing was in flight — so a repository that FAILS the fleet gate, which is\n * every repository with work in it, was handed a green-looking list that never\n * once named the command CI runs. This form grades nothing; the least it can do\n * is say where grading happens. `next.fleet-clean` is also no longer the\n * empty-repo answer: over zero services, \"every service is written down\" is\n * vacuously true, and the first hour of a docs repo used to get that green\n * sentence instead of its first three steps. The teaching ladder below owns\n * that case now, and the clean entry survives only for its truthful one —\n * services exist, and nothing is owed.\n */\n/** The whole fleet as the next-step walk reads it. */\nexport interface Fleet {\n services: ServiceEntry[];\n features: FleetFeatureState[];\n graph: DependencyGraph;\n interrupted: InterruptedCommit | null;\n /**\n * The ladder's fact, present exactly when the ladder can apply — an\n * unnarrowed run over an empty fleet. `null` otherwise: on a narrowed run\n * (withheld for the reason fleet.ts records beside the binding) and on any\n * fleet with services or features, where fleet.ts skips the read entirely.\n */\n teaching: { landscape: \"missing\" | \"stub\" | \"authored\" } | null;\n}\n\n/**\n * What this repository says it IS, as the walk needs it — and it needs BOTH\n * facts, which is why this is a record and not the bare id it used to be.\n *\n * `adopted: false` is the case the walk always handled: the fleet has no\n * `services/<id>/`, and `next.adopt-bound` leads. `adopted: true` was invisible\n * — the parameter was `string | null` and went null exactly when the directory\n * existed — so a repository bound to a service the fleet DOES know got the\n * whole system's worklist in alphabetical order, with its own boundary\n * somewhere in the middle of it or, past ten services, not on the list at all.\n */\nexport interface Binding {\n id: string;\n /** Whether the fleet has a `services/<id>/` for it. */\n adopted: boolean;\n}\n\nexport function fleetNext(fleet: Fleet, binding: Binding | null): NextStep[] {\n const { services, features, graph, interrupted, teaching } = fleet;\n const steps: NextStep[] = [];\n // The two arms of the binding, each named for the question it answers. The\n // first reproduces the old parameter exactly, so everything reading it below\n // is unchanged; the second is the fact that used to be thrown away.\n const unadoptedBinding = binding !== null && !binding.adopted ? binding.id : null;\n const adoptedBinding = binding !== null && binding.adopted ? binding.id : null;\n\n // The repository this command is standing in names a service the fleet has no\n // directory for. First, ahead of every other service's partial adoption,\n // because it is the only step that is about HERE.\n //\n // This form used to count the fleet and ignore the binding entirely, so in a\n // service repo bound to an unadopted service — a freshly wired one, the most\n // common repo there is — an empty fleet made \"every service is written down\"\n // vacuously true and `next.fleet-clean` came out top. `loam doctor` had the\n // right answer (`doctor.service-unknown`) at the same moment, and the\n // documented agent loop reads `status`: an agent following it was sent to\n // start a feature instead of adopting the service under its feet.\n if (unadoptedBinding !== null) {\n steps.push({\n code: \"next.adopt-bound\",\n statement:\n `This repository's loam.json says it is '${unadoptedBinding}', and the fleet has no ` +\n `services/${unadoptedBinding}/ — nothing about the service you are standing in is written down yet.`,\n command: `loam adopt --service ${unadoptedBinding} --json`,\n service: unadoptedBinding,\n });\n }\n\n // The first hour of a docs repo, in order. Zero services and zero features\n // used to fall through every loop below into `next.fleet-clean` — the header\n // says why that answer was vacuous — so the emptiest repository loam serves\n // got the least from the command whose whole purpose is \"what do I do next\".\n // These are ordinary steps on purpose: the gate entry still closes the list,\n // and the tail ternary is untouched. The bind/adopt rungs are suppressed when\n // `next.adopt-bound` above already names the exact adopt to run, and the\n // whole ladder is withheld on narrowed runs (`teaching === null`).\n if (teaching !== null && services.length === 0 && features.length === 0) {\n if (teaching.landscape !== \"authored\") {\n steps.push({\n code: \"next.author-landscape\",\n statement:\n teaching.landscape === \"missing\"\n ? \"architecture/landscape.likec4 does not exist — the fleet map is the one artifact every \" +\n \"cross-service check reads. Run `loam init --create` to scaffold it, then draw the first service.\"\n : \"architecture/landscape.likec4 is still the scaffold's untouched map — no service is drawn. \" +\n \"State the facts in a fleet.yaml (service ids, `a -> b` calls) and run \" +\n \"`loam seed --from fleet.yaml` to template the map mechanically — or open the file \" +\n \"and follow its own comments. Either way loam never guesses who calls whom.\",\n // The command each arm's own prose already names, and for a while it\n // was neither: both arms carried the gate. WORKFLOW.md documents the\n // agent loop as \"read next[0], run the command it names, repeat\" and\n // every generated skill says to branch on the code rather than the\n // prose — so an agent on the first rung of the first hour ran\n // `validate --all` against a repository with no services in it, which\n // answers ok:true with `landscape.matched` and changes nothing, and\n // came back to the identical next[0] forever. A livelock on day zero,\n // on the one command built to answer \"what now\". The gate is still\n // named: `next.fleet-gate` closes this same list.\n command: teaching.landscape === \"missing\" ? \"loam init --create\" : \"loam seed --from fleet.yaml\",\n path: \"architecture/landscape.likec4\",\n });\n }\n if (unadoptedBinding === null) {\n steps.push({\n code: \"next.bind-service\",\n statement:\n \"No service repository is bound to this fleet yet. From a service's own repo, point --docs \" +\n \"here and --service at its canonical id, then commit the loam.json it writes.\",\n command: \"loam init --docs <path-to-this-docs-repo> --service <service-id>\",\n });\n steps.push({\n code: \"next.adopt-first\",\n statement:\n \"services/ is empty — nothing about any service is written down yet. Adopt the first one: \" +\n \"the brief walks an agent through writing its baseline docs as draft.\",\n command: \"loam adopt --service <service-id> --json\",\n });\n }\n }\n\n // The per-service rungs are collected into their own list rather than pushed\n // straight onto `steps`, because the partition below acts on them as a group\n // — and only on them. Nothing here may float above `next.adopt-bound` or the\n // first-hour ladder, both of which are about the repository rather than about\n // any one service in the fleet.\n const perService: NextStep[] = [];\n for (const s of services.filter(undocumented)) {\n perService.push({\n code: \"next.adopt\",\n statement: `${serviceTreePath(s)}/ has no spec.md — nothing about ${s.id} is written down, so no feature can be graded against it.`,\n command: `loam adopt --service ${s.id} --json`,\n service: s.id,\n });\n }\n\n // Adoption does not end at spec.md. A service with a living spec and no\n // model.likec4 is the state archive itself warns about (`service.no-model`)\n // and the fleet gate reports — invisible here until now, because this form\n // looked at exactly one bit of each service's artifact set.\n for (const s of services.filter((x) => !undocumented(x) && !x.has.model)) {\n perService.push({\n code: \"next.complete-service\",\n statement: `${serviceTreePath(s)}/ has a spec.md but no model.likec4 — the fleet gate reports the service incomplete until something models it.`,\n command: `loam validate --service ${s.id} --json`,\n service: s.id,\n });\n }\n\n // The boundary this repository IS comes first among the boundaries it merely\n // shares a fleet with. A stable PARTITION and deliberately not a sort: the\n // bound service's rungs move to the front and every other service keeps the\n // alphabetical order it already had, so the list an unbound reader sees — a\n // docs repo, CI, `loam validate --all`'s scorecard — is byte-for-byte the one\n // it saw before.\n //\n // The cap is why this is ordering and not cosmetics. Ten rungs print, and in\n // the ten-repo case WORKFLOW.md describes every developer's `loam status`\n // opened with nine other services' adoption work; past ten services the one\n // rung this developer can actually act on was elided entirely, leaving a list\n // of nothing but other people's boundaries under the heading \"next\". No new\n // code is emitted for any of this — `next[]` has always been documented as\n // most-unblocking first, and this is the line that makes the claim true from\n // where the reader is standing.\n steps.push(\n ...(adoptedBinding === null\n ? perService\n : [\n ...perService.filter((s) => s.service === adoptedBinding),\n ...perService.filter((s) => s.service !== adoptedBinding),\n ]),\n );\n\n const ordered = [...features].sort(\n (a, b) =>\n STAGE_RANK[a.stage] - STAGE_RANK[b.stage] ||\n graph.order.indexOf(a.id) - graph.order.indexOf(b.id) ||\n compareIds(a.id, b.id),\n );\n for (const f of ordered) {\n if (f.stage === \"done\") {\n steps.push({\n code: \"next.archive\",\n statement: `${f.id} is authored and verified — ship it, and everything waiting on it is released.`,\n command: `loam archive ${f.id} --dry-run --json`,\n });\n continue;\n }\n steps.push({\n code: \"next.feature\",\n statement:\n // The list, not the stage, is what this sentence names. `blocked` has\n // two causes now — another feature in flight, and an interrupted commit\n // that stalls the whole repository — and the second one names no\n // feature, so keying off the stage alone printed \"FEAT-1 is waiting\n // on .\" The recover step above already says what the repository is\n // waiting on.\n f.stage === \"blocked\" && f.blockedBy.length > 0\n ? `${f.id} is waiting on ${f.blockedBy.join(\", \")}.`\n : `${f.id} is at '${f.stage}'${f.missing.length > 0 ? ` — missing ${f.missing.join(\", \")}` : \"\"}.`,\n command: `loam status ${f.id} --json`,\n });\n }\n\n const elided = Math.max(0, steps.length - FLEET_NEXT_LIMIT);\n // Outside the cap, not merely first inside it: the one step that must never\n // be the one this list left out.\n const out = [...(interrupted === null ? [] : [recoverStep(interrupted)]), ...steps.slice(0, FLEET_NEXT_LIMIT)];\n if (elided > 0) {\n out.push({\n code: \"next.elided\",\n statement: `${elided} more step(s) of the same kinds were left out — this list is ordered most-unblocking first, so work down it and run status again.`,\n command: \"loam list --json\",\n });\n }\n\n out.push(\n steps.length === 0\n ? {\n code: \"next.fleet-clean\",\n statement:\n \"Nothing is in flight and every service is written down — keep the fleet green, then start the next feature.\",\n command: \"loam validate --all --json\",\n }\n : {\n code: \"next.fleet-gate\",\n // `loam validate --all` inside the backticks, not the bare\n // `validate --all` this sentence carried for a year: a span without\n // the binary name is invisible to every check that reads a\n // backticked `loam <verb>` as an instruction — the parse check in\n // test/agent-commands-runnable.test.ts, and the statement/command\n // invariant in test/status.test.ts. A prefix is cheaper than an\n // exemption, and the exemption is what lets a wrong command through.\n statement:\n `${features.length} feature(s) in flight over ${services.length} service(s) — ` +\n \"status grades none of them; `loam validate --all` is the gate this repository has to pass, and it is what CI runs.\",\n command: \"loam validate --all --json\",\n },\n );\n return out;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"next.js","sourceRoot":"","sources":["../../../../src/core/status/fleet/next.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `next[]` — the whole repository: adoption gaps first, then every feature in\n * flight, most-unblocking first, capped so the list stays navigation rather\n * than inventory.\n */\nimport type { DependencyGraph } from \"../../dependencies/facts.js\";\nimport { serviceTreePath } from \"../../kernel/ids/dirs.js\";\nimport { compareIds, type ServiceEntry } from \"../../repo/entries.js\";\nimport { recoverStep } from \"../interrupted.js\";\nimport {\n undocumented,\n vouched,\n type ArtifactStatus,\n type FleetFeatureState,\n type InterruptedCommit,\n type NextStep,\n} from \"../report.js\";\n\n/** How urgent a stage is at fleet level. See {@link fleetNext}. */\nconst STAGE_RANK: Record<ArtifactStatus, number> = {\n done: 0,\n ready: 1,\n draft: 2,\n missing: 3,\n blocked: 4,\n};\n\n/**\n * How many per-service and per-feature steps the fleet form will print before\n * it stops and says how many it left out. A fleet mid-adoption produced 135\n * entries under the heading \"the next thing to do\" — a list nobody reads is a\n * list that is not navigation, and the first ten are the ten that matter\n * because the ordering below is already most-unblocking-first.\n *\n * Exported so the test asserts the cap through the constant: a literal 10 in a\n * fixture is a second declaration of the limit, free to disagree the day this\n * one moves.\n */\nexport const FLEET_NEXT_LIMIT = 10;\n\n/**\n * Fleet-level steps, most-unblocking first. A feature ready to ship comes\n * before one that still needs authoring, because archiving it is what releases\n * everything queued behind it; a blocked feature comes last, because there is\n * nothing to do about it from here — its prerequisite is already higher up the\n * list under its own entry.\n *\n * The gate is the LAST entry and it is always there. `next.fleet-clean` used to\n * be the only route to `loam validate --all`, and it was emitted only when\n * nothing was in flight — so a repository that FAILS the fleet gate, which is\n * every repository with work in it, was handed a green-looking list that never\n * once named the command CI runs. This form grades nothing; the least it can do\n * is say where grading happens. `next.fleet-clean` is also no longer the\n * empty-repo answer: over zero services, \"every service is written down\" is\n * vacuously true, and the first hour of a docs repo used to get that green\n * sentence instead of its first three steps. The teaching ladder below owns\n * that case now, and the clean entry survives only for its truthful one —\n * services exist, and nothing is owed.\n */\n/** The whole fleet as the next-step walk reads it. */\nexport interface Fleet {\n services: ServiceEntry[];\n features: FleetFeatureState[];\n graph: DependencyGraph;\n interrupted: InterruptedCommit | null;\n /**\n * The ladder's fact, present exactly when the ladder can apply — an\n * unnarrowed run over an empty fleet. `null` otherwise: on a narrowed run\n * (withheld for the reason fleet.ts records beside the binding) and on any\n * fleet with services or features, where fleet.ts skips the read entirely.\n */\n teaching: { landscape: \"missing\" | \"stub\" | \"authored\" } | null;\n}\n\n/**\n * What this repository says it IS, as the walk needs it — and it needs BOTH\n * facts, which is why this is a record and not the bare id it used to be.\n *\n * `adopted: false` is the case the walk always handled: the fleet has no\n * `services/<id>/`, and `next.adopt-bound` leads. `adopted: true` was invisible\n * — the parameter was `string | null` and went null exactly when the directory\n * existed — so a repository bound to a service the fleet DOES know got the\n * whole system's worklist in alphabetical order, with its own boundary\n * somewhere in the middle of it or, past ten services, not on the list at all.\n */\nexport interface Binding {\n id: string;\n /** Whether the fleet has a `services/<id>/` for it. */\n adopted: boolean;\n}\n\nexport function fleetNext(fleet: Fleet, binding: Binding | null): NextStep[] {\n const { services, features, graph, interrupted, teaching } = fleet;\n const steps: NextStep[] = [];\n // The two arms of the binding, each named for the question it answers. The\n // first reproduces the old parameter exactly, so everything reading it below\n // is unchanged; the second is the fact that used to be thrown away.\n const unadoptedBinding = binding !== null && !binding.adopted ? binding.id : null;\n const adoptedBinding = binding !== null && binding.adopted ? binding.id : null;\n\n // The repository this command is standing in names a service the fleet has no\n // directory for. First, ahead of every other service's partial adoption,\n // because it is the only step that is about HERE.\n //\n // This form used to count the fleet and ignore the binding entirely, so in a\n // service repo bound to an unadopted service — a freshly wired one, the most\n // common repo there is — an empty fleet made \"every service is written down\"\n // vacuously true and `next.fleet-clean` came out top. `loam doctor` had the\n // right answer (`doctor.service-unknown`) at the same moment, and the\n // documented agent loop reads `status`: an agent following it was sent to\n // start a feature instead of adopting the service under its feet.\n if (unadoptedBinding !== null) {\n steps.push({\n code: \"next.adopt-bound\",\n statement:\n `This repository's loam.json says it is '${unadoptedBinding}', and the fleet has no ` +\n `services/${unadoptedBinding}/ — nothing about the service you are standing in is written down yet.`,\n command: `loam adopt --service ${unadoptedBinding} --json`,\n service: unadoptedBinding,\n });\n }\n\n // The first hour of a docs repo, in order. Zero services and zero features\n // used to fall through every loop below into `next.fleet-clean` — the header\n // says why that answer was vacuous — so the emptiest repository loam serves\n // got the least from the command whose whole purpose is \"what do I do next\".\n // These are ordinary steps on purpose: the gate entry still closes the list,\n // and the tail ternary is untouched. The bind/adopt rungs are suppressed when\n // `next.adopt-bound` above already names the exact adopt to run, and the\n // whole ladder is withheld on narrowed runs (`teaching === null`).\n if (teaching !== null && services.length === 0 && features.length === 0) {\n if (teaching.landscape !== \"authored\") {\n steps.push({\n code: \"next.author-landscape\",\n statement:\n teaching.landscape === \"missing\"\n ? \"architecture/landscape.likec4 does not exist — the fleet map is the one artifact every \" +\n \"cross-service check reads. Run `loam init --create` to scaffold it, then draw the first service.\"\n : \"architecture/landscape.likec4 is still the scaffold's untouched map — no service is drawn. \" +\n \"State the facts in a fleet.yaml (service ids, `a -> b` calls) and run \" +\n \"`loam seed --from fleet.yaml` to template the map mechanically — or open the file \" +\n \"and follow its own comments. Either way loam never guesses who calls whom.\",\n // The command each arm's own prose already names, and for a while it\n // was neither: both arms carried the gate. WORKFLOW.md documents the\n // agent loop as \"read next[0], run the command it names, repeat\" and\n // every generated skill says to branch on the code rather than the\n // prose — so an agent on the first rung of the first hour ran\n // `validate --all` against a repository with no services in it, which\n // answers ok:true with `landscape.matched` and changes nothing, and\n // came back to the identical next[0] forever. A livelock on day zero,\n // on the one command built to answer \"what now\". The gate is still\n // named: `next.fleet-gate` closes this same list.\n command: teaching.landscape === \"missing\" ? \"loam init --create\" : \"loam seed --from fleet.yaml\",\n path: \"architecture/landscape.likec4\",\n });\n }\n if (unadoptedBinding === null) {\n steps.push({\n code: \"next.bind-service\",\n statement:\n \"No service repository is bound to this fleet yet. From a service's own repo, point --docs \" +\n \"here and --service at its canonical id with the service agent profile, then commit the \" +\n \"loam.json it writes.\",\n // `--agent-profile service`, because the default is `full`, and `full`\n // writes loam-feature and loam-ship into the service checkout — the two\n // workflows this docs repo keeps, which README tells every service\n // repo to leave out. An agent runs this literal, not the README.\n command: \"loam init --docs <path-to-this-docs-repo> --service <service-id> --agent-profile service\",\n });\n steps.push({\n code: \"next.adopt-first\",\n statement:\n \"services/ is empty — nothing about any service is written down yet. Adopt the first one: \" +\n \"the brief walks an agent through writing its baseline docs as draft.\",\n command: \"loam adopt --service <service-id> --json\",\n });\n }\n }\n\n // The per-service rungs are collected into their own list rather than pushed\n // straight onto `steps`, because the partition below acts on them as a group\n // — and only on them. Nothing here may float above `next.adopt-bound` or the\n // first-hour ladder, both of which are about the repository rather than about\n // any one service in the fleet.\n const perService: NextStep[] = [];\n for (const s of services.filter(undocumented)) {\n perService.push({\n code: \"next.adopt\",\n statement: `${serviceTreePath(s)}/ has no spec.md — nothing about ${s.id} is written down, so no feature can be graded against it.`,\n command: `loam adopt --service ${s.id} --json`,\n service: s.id,\n });\n }\n\n // Adoption does not end at spec.md. A service with a living spec and no\n // model.likec4 is the state archive itself warns about (`service.no-model`)\n // and the fleet gate reports — invisible here until now, because this form\n // looked at exactly one bit of each service's artifact set.\n for (const s of services.filter((x) => !undocumented(x) && !x.has.model)) {\n perService.push({\n code: \"next.complete-service\",\n statement: `${serviceTreePath(s)}/ has a spec.md but no model.likec4 — the fleet gate reports the service incomplete until something models it.`,\n command: `loam validate --service ${s.id} --json`,\n service: s.id,\n });\n }\n\n // A vouch the document has moved out from under. `content.stale` needs\n // nothing but the document, so unlike `sources.stale` this form CAN see it —\n // and before it looked, a fleet whose specs were edited after the person read\n // them got `next.fleet-clean`: \"nothing is owed\" over words nobody had\n // re-read, while `loam validate` warned about each one. The re-vouch is a\n // person's act from that service's own repository (`loam vouch` restamps the\n // code side too), so the step names the documents and that repository, and\n // its execution contract is a human review rather than a command to run.\n for (const s of services.filter((x) => vouched(x) && x.contentStale.length > 0)) {\n const files = s.contentStale.map((file) => `${serviceTreePath(s)}/${file}`);\n perService.push({\n code: \"next.revouch\",\n statement:\n `${files.join(\" and \")} changed since ${s.id} was vouched — \\`status: verified\\` stands over words nobody has re-read (content.stale). ` +\n `A person re-reads what moved (\\`loam vouch --pack --service ${s.id}\\` lists it) and re-vouches from ${s.id}'s own repository.`,\n command: `loam vouch --service ${s.id}`,\n service: s.id,\n });\n }\n\n // The boundary this repository IS comes first among the boundaries it merely\n // shares a fleet with. A stable PARTITION and deliberately not a sort: the\n // bound service's rungs move to the front and every other service keeps the\n // alphabetical order it already had, so the list an unbound reader sees — a\n // docs repo, CI, `loam validate --all`'s scorecard — is byte-for-byte the one\n // it saw before.\n //\n // The cap is why this is ordering and not cosmetics. Ten rungs print, and in\n // the ten-repo case WORKFLOW.md describes every developer's `loam status`\n // opened with nine other services' adoption work; past ten services the one\n // rung this developer can actually act on was elided entirely, leaving a list\n // of nothing but other people's boundaries under the heading \"next\". No new\n // code is emitted for any of this — `next[]` has always been documented as\n // most-unblocking first, and this is the line that makes the claim true from\n // where the reader is standing.\n steps.push(\n ...(adoptedBinding === null\n ? perService\n : [\n ...perService.filter((s) => s.service === adoptedBinding),\n ...perService.filter((s) => s.service !== adoptedBinding),\n ]),\n );\n\n const ordered = [...features].sort(\n (a, b) =>\n STAGE_RANK[a.stage] - STAGE_RANK[b.stage] ||\n graph.order.indexOf(a.id) - graph.order.indexOf(b.id) ||\n compareIds(a.id, b.id),\n );\n for (const f of ordered) {\n if (f.stage === \"done\") {\n steps.push({\n code: \"next.archive\",\n statement: `${f.id} is authored and verified — ship it, and everything waiting on it is released.`,\n command: `loam archive ${f.id} --dry-run --json`,\n });\n continue;\n }\n steps.push({\n code: \"next.feature\",\n statement:\n // The list, not the stage, is what this sentence names. `blocked` has\n // two causes now — another feature in flight, and an interrupted commit\n // that stalls the whole repository — and the second one names no\n // feature, so keying off the stage alone printed \"FEAT-1 is waiting\n // on .\" The recover step above already says what the repository is\n // waiting on.\n f.stage === \"blocked\" && f.blockedBy.length > 0\n ? `${f.id} is waiting on ${f.blockedBy.join(\", \")}.`\n : `${f.id} is at '${f.stage}'${f.missing.length > 0 ? ` — missing ${f.missing.join(\", \")}` : \"\"}.`,\n command: `loam status ${f.id} --json`,\n });\n }\n\n const elided = Math.max(0, steps.length - FLEET_NEXT_LIMIT);\n // Outside the cap, not merely first inside it: the one step that must never\n // be the one this list left out.\n const out = [...(interrupted === null ? [] : [recoverStep(interrupted)]), ...steps.slice(0, FLEET_NEXT_LIMIT)];\n if (elided > 0) {\n out.push({\n code: \"next.elided\",\n statement: `${elided} more step(s) of the same kinds were left out — this list is ordered most-unblocking first, so work down it and run status again.`,\n command: \"loam list --json\",\n });\n }\n\n out.push(\n steps.length === 0\n ? {\n code: \"next.fleet-clean\",\n statement:\n \"Nothing is in flight and every service is written down — keep the fleet green, then start the next feature.\",\n command: \"loam validate --all --json\",\n }\n : {\n code: \"next.fleet-gate\",\n // `loam validate --all` inside the backticks, not the bare\n // `validate --all` this sentence carried for a year: a span without\n // the binary name is invisible to every check that reads a\n // backticked `loam <verb>` as an instruction — the parse check in\n // test/agent-commands-runnable.test.ts, and the statement/command\n // invariant in test/status.test.ts. A prefix is cheaper than an\n // exemption, and the exemption is what lets a wrong command through.\n statement:\n `${features.length} feature(s) in flight over ${services.length} service(s) — ` +\n \"status grades none of them; `loam validate --all` is the gate this repository has to pass, and it is what CI runs.\",\n command: \"loam validate --all --json\",\n },\n );\n return out;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/core/status/report.ts"],"names":[],"mappings":"AA8EA,wEAAwE;AACxE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAU,CAAC;AAG3F;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,QAAQ;IACR,OAAO;IACP,cAAc;IACd,MAAM;IACN,WAAW;IACX,SAAS;IACT,UAAU;IACV,cAAc;CACN,CAAC;AAqPX,+FAA+F;AAC/F,MAAM,UAAU,YAAY,CAAC,CAAe;IAC1C,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,OAAO,CAAC,CAAe;IACrC,OAAO,CAAC,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;AACtD,CAAC","sourcesContent":["/**\n * Where does this work stand, and what is the next thing to do?\n *\n * Every other command answers a question you already know how to ask: validate\n * grades a target you name, delta briefs a service you name, verify checks a\n * feature you name. The one thing loam could not answer was the question an\n * agent asks FIRST — the one it has when it joins a repository halfway, or\n * comes back after losing its session. That knowledge existed, but only as\n * prose inside the slash commands (\"1. scaffold, 2. author, 3. validate\"),\n * which is to say it existed for whoever had read them recently and for nobody\n * else.\n *\n * The obvious implementation is a state file — a `.loam/state.json` recording\n * which step each feature reached. It is rejected here for the reason core/verify/\n * refuses a second mutable database: the moment a second record of the truth\n * exists, an author who edits a file by hand, a merge, a rebase or a\n * `git checkout` desynchronises it, and the state file's answer is confidently\n * wrong exactly when somebody trusts it. So `status` stores nothing and caches\n * nothing. It is a projection: every invocation re-derives the whole answer\n * from the files, and a file edited in another window is visible on the next\n * run with no invalidation step for anyone to forget.\n *\n * It also grades nothing itself. Every verdict here is somebody else's\n * function: `featureCoherence` (what `loam archive` refuses on) plus\n * `featureProvenance` and `requirementsMissingScenarios` (`loam validate\n * --feature`'s other error sources, as far as they exist as shared functions),\n * and `readVerificationState` + `featureChecklist`, the pair `loam verify`\n * uses. A second opinion about whether a feature holds together would be a\n * second answer to grade a pull request against, and the one a reader consults\n * is never the one the gate obeys (doctor.ts learnt this with its own config\n * validator). A check that lives inline inside `validate` cannot be inherited\n * at all, which is why `stage` never rests on `checks.coherent` alone.\n *\n * Which is why the rule this module is held to is one-directional: **status may\n * be more pessimistic than the gates, never greener.** `validate` and `archive`\n * answer two different questions — is the DOCUMENT valid, is the MERGE safe\n * (issue.ts) — and a projection that reported only one of them told an author\n * \"ship it\" about a feature the other one refuses. So `stage` and `next[]` take\n * the UNION: an error `validate` grades and a warning `archive` gates on both\n * make a feature `draft`, and test/status-agrees-with-gate.test.ts holds the\n * direction on a matrix of deliberately-damaged trees.\n *\n * ## The status vocabulary\n *\n * Five values, closed, and an agent is expected to branch on them:\n *\n * `missing` the artifact is owed and is not on disk; nothing stands in the\n * way of writing it now.\n * `blocked` the work is stalled on something that is NOT this artifact — an\n * artifact it is derived from is absent, or another feature in\n * flight has to archive first.\n * `draft` it is on disk and the shared checks report an error against it:\n * what exists is wrong.\n * `ready` it is on disk and clean, but its obligation is not discharged —\n * something outside the documents (code, a test run, a recording)\n * still has to answer it.\n * `done` nothing is owed here.\n *\n * The two pairs that look alike are the point of the set. `missing` and\n * `blocked` both mean \"not written\", and they are different instructions: one\n * says write it now, the other says you cannot yet and names what comes first —\n * an agent that cannot tell them apart authors a verification record against a\n * checklist that does not exist. `draft` and `ready` both mean \"on disk\", and\n * they are different instructions too: `draft` says the document is wrong and\n * the fix is in this repository, `ready` says the document is right and the\n * world has not caught up — the code is not built, or the claims are not\n * answered. Collapsing those two would send an agent to re-edit a spec that is\n * already correct.\n *\n * A value unreachable in a scope is simply never emitted there, deliberately:\n * the fleet form never says `draft`, because grading is a per-feature cost it\n * refuses to pay.\n */\nimport type { ServiceEntry } from \"../repo/entries.js\";\nimport type { UseCaseBlastRadius } from \"../usecases/touch.js\";\nimport type { VerificationVerdict } from \"../verify/record.js\";\nimport type { Finding } from \"../vocabulary/report.js\";\n\n/** The closed set. The module header says what each value instructs. */\nexport const ARTIFACT_STATUSES = [\"missing\", \"blocked\", \"draft\", \"ready\", \"done\"] as const;\nexport type ArtifactStatus = (typeof ARTIFACT_STATUSES)[number];\n\n/**\n * The artifact kinds a feature can owe, in the order the cycle authors them.\n * `spec`, `arch-spec`, `openapi` and `asyncapi` are per-service and repeat\n * once per service the feature touches; `capabilities` repeats once per\n * capability document the feature's delta carries; the rest are feature-wide.\n *\n * `capabilities` sits where an analyst writes it — the business promise comes\n * before the services that keep it, and a capability-only feature is a real and\n * ordinary first commit.\n */\nexport const ARTIFACT_IDS = [\n \"intent\",\n \"delta\",\n \"capabilities\",\n \"spec\",\n \"arch-spec\",\n \"openapi\",\n \"asyncapi\",\n \"verification\",\n] as const;\nexport type ArtifactId = (typeof ARTIFACT_IDS)[number];\n\nexport interface ArtifactState {\n id: ArtifactId;\n /** The service this artifact belongs to, or null for the feature-wide ones. */\n service: string | null;\n /**\n * The capability this artifact belongs to, on `capabilities` rows only.\n *\n * A field of its own rather than a capability id put in `service`: the two\n * are different namespaces — a fleet may hold a service and a capability of\n * one name — and bending a published field to carry the other kind is how a\n * consumer grouping by service silently acquires a row that is not one. Absent\n * on every other row, so nothing that reads `service` today changes.\n */\n capability?: string;\n /** Repo-relative with forward slashes — diffable across machines, like every path in the contract. */\n path: string;\n exists: boolean;\n /**\n * Whether ABSENCE is a defect. False for the artifacts a feature may\n * legitimately not have: `delta.likec4` (a requirements-only change deletes\n * it), `arch.spec.md` and `asyncapi.yaml` (optional everywhere), and\n * `openapi.yaml` for a service\n * the living docs already carry. An absent optional artifact is `done` —\n * nothing is owed — and `exists: false` beside it is how a reader tells that\n * from a file that is present and fine.\n */\n required: boolean;\n status: ArtifactStatus;\n /** Artifact ids that must come first. Non-empty only when `status` is `blocked`. */\n blockedBy: string[];\n}\n\nexport interface NextStep {\n /** Stable — prose may change, this may not. */\n code: string;\n /** One sentence, actionable without reading any other field. */\n statement: string;\n /** The literal command to run. */\n command: string;\n artifact?: ArtifactId;\n service?: string;\n /** The repo-relative file the step is about, when it names one. */\n path?: string;\n}\n\nexport type NextActionKind = \"command\" | \"edit\" | \"external-repo\" | \"human-review\";\nexport type NextActionCwd = \"configured-repo\" | \"docs-repo\" | \"service-repo\";\n\n/**\n * The machine contract beside the legacy `NextStep.command` string.\n *\n * `command` exists only when every argument is concrete. An edit names the\n * file and puts the old command in `after`, because that command grades the\n * authored work rather than performing it. `needs` is data the caller must\n * obtain; it is never smuggled into a shell-looking placeholder.\n */\nexport interface NextExecution {\n kind: NextActionKind;\n runnable: boolean;\n cwd: NextActionCwd;\n command?: string;\n after?: string;\n needs?: string[];\n}\n\nexport type ExecutableNextStep = NextStep & { execution: NextExecution };\n\n/**\n * What the record beside the feature says, without asking whether anyone should\n * believe the code. `stale` is its own state and not merely \"recorded\": the\n * feature moved after the answers were written, so the record answers questions\n * that are no longer the ones being asked — which is exactly why `loam verify\n * --record` refuses to merge into it.\n */\nexport interface VerificationState {\n state: \"absent\" | \"unreadable\" | \"stale\" | \"recorded\";\n /** The day the record was written, when there is a readable one. */\n recorded: string | null;\n /**\n * `verify --json`'s own three-valued verdict, through `verificationVerdict` —\n * not a fourth opinion computed here. `attested` is the one that matters to a\n * reader of this command: complete, and resting on somebody's word about a\n * test rather than on a run.\n */\n verdict: VerificationVerdict;\n /** Every count below is `tallyRecord` — the record's `claims:` array, never its `summary:` block. */\n claims: number;\n confirmed: number;\n unconfirmed: number;\n /** Federated records only: claims nobody has answered yet. */\n unanswered: number;\n /** Of the confirmed, the scenario claims on an agent's word (`attestedClaims`). */\n attested: number;\n}\n\n/**\n * A `loam archive`/`unarchive` that was killed mid-commit, read off the intent\n * journal `.loam-commit` leaves in the docs repo.\n *\n * It belongs in a projection that grades nothing because it is not a grade: it\n * is the one repository state where every OTHER answer this command gives is\n * about files that may be half-written. `doctor` blocks on it; `status` is what\n * an agent runs first, and it reported `done` over a half-merged repo — so it\n * says so too, from the same journal, and leads its steps with the repair.\n *\n * Read, never repaired: recovery happens under the docs lock inside `archive`\n * and `unarchive`, and `status` writes nothing and takes no lock.\n */\nexport interface InterruptedCommit {\n /**\n * Which command was committing, or null when the journal cannot be read at\n * all. A sentence for a human, never a branch: version-2 journals carry any\n * writer's name (rebase, vouch, new, gherkin), so an enum here would be a\n * list this type has to chase.\n */\n command: string | null;\n feature: string | null;\n /** Who and when, so a human can place it against their own shell history. */\n host: string | null;\n pid: number | null;\n at: string | null;\n /** Docs-repo-relative paths that may hold half-written bytes. */\n files: string[];\n /** `.loam-commit` exists but does not parse — the worst case, because nothing can grade it. */\n unreadable: boolean;\n /** The command that recovers, or null when only a human can. */\n recover: string | null;\n}\n\nexport interface FeatureStatusReport {\n /**\n * A killed commit in this docs repo, or null. Present on both forms: it is a\n * fact about the repository, and every artifact row below it is a fact about\n * files that repository may have half-written.\n */\n interrupted: InterruptedCommit | null;\n feature: {\n id: string;\n dirName: string;\n path: string;\n archived: boolean;\n /** The feature's own rollup, from the same vocabulary as its artifacts. */\n stage: ArtifactStatus;\n /** Every service the feature touches, whatever `--service` narrowed the view to. */\n services: string[];\n /** Active features that must archive before this one, from the dependency index. */\n blockedBy: string[];\n };\n /** The `--service` narrowing, or null when the whole feature is in view. */\n service: string | null;\n artifacts: ArtifactState[];\n /**\n * The grade, from the same calls the two gates make: `featureCoherence`\n * (archive), plus `featureProvenance` and the scenario-coverage check — the\n * error sources `validate --feature` shares as functions. `ran` is false for\n * an archived feature: its delta is already merged into the living docs, so\n * re-grading it would compare a change against a world that has absorbed it.\n */\n checks: {\n ran: boolean;\n /**\n * No error among the findings above — `loam validate --feature`'s verdict\n * for every error source that is a shared function. It is not on its own\n * what makes a feature shippable: `stage` and `next[]` take the union with\n * archive's gating set, because a warning that gates is a warning this\n * field reports as coherent.\n */\n coherent: boolean;\n errors: number;\n warnings: number;\n /** How many findings would stop `loam archive` without `--approve`. */\n gating: number;\n /**\n * Findings, not Issues: the set is wider than coherence now, and `gates` is\n * resolved on every coherence finding so a consumer never re-implements the\n * severity default. One breach reported by `validate` and by `status` has\n * to arrive in one shape.\n */\n issues: Finding[];\n };\n verification: VerificationState;\n /**\n * The declared business flows the services IN VIEW are hops of — blast\n * radius, and the only field here that is a fact about the fleet rather than\n * about the feature.\n *\n * It narrows with `--service` exactly as `artifacts` and `next` do, because\n * `--service` is a lens on what the reader is being asked to do next. What it\n * must never do is go silently empty: `unreadable` is how it says that\n * `architecture/` could not be read, which is a different fact from a fleet\n * that draws no use cases and produces the identical empty list.\n *\n * The FLEET form has no equivalent, deliberately. That form grades nothing and\n * is coarse by design, and a per-feature flow join over every feature in flight\n * is exactly the per-feature cost it refuses to pay.\n */\n useCases: UseCaseBlastRadius;\n next: NextStep[];\n}\n\nexport interface FleetFeatureState {\n id: string;\n dirName: string;\n path: string;\n stage: ArtifactStatus;\n services: string[];\n blockedBy: string[];\n /** Artifacts the feature owes and has not written. Presence only — the fleet form grades nothing. */\n missing: string[];\n verification: VerificationState;\n}\n\nexport interface FleetStatusReport {\n /** See {@link FeatureStatusReport.interrupted}. */\n interrupted: InterruptedCommit | null;\n /**\n * Three disjoint buckets over `services/`, summing to `total`. Deliberately\n * coarser than `list`'s maturity ladder, and deliberately not the same words:\n * that ladder grades an adoption campaign rung by rung, this asks only\n * whether there is anything here to build a feature on.\n */\n services: {\n total: number;\n /** No `spec.md` — nothing about this service is written down. */\n undocumented: number;\n /** Documented, with no vouch stamp behind it. */\n draft: number;\n /** `status: verified` with a `sources_digest` — a person stood behind it. */\n vouched: number;\n /**\n * Of `vouched`, the ones read from a recorded SAMPLE rather than in full\n * (`vouch_scope`, either axis) — a SUBSET: the three above still sum to `total`.\n */\n sampledVouched: number;\n };\n features: FleetFeatureState[];\n /** Dependencies before consumers — which feature has to archive first. */\n order: string[];\n service: string | null;\n next: NextStep[];\n}\n\n/** Nothing is written down: no living spec at all. Every other absence is partial adoption. */\nexport function undocumented(s: ServiceEntry): boolean {\n return !s.has.spec;\n}\n\n/** A person stamped it — `loam vouch`'s exact postcondition, read back off the frontmatter. */\nexport function vouched(s: ServiceEntry): boolean {\n return s.status === \"verified\" && s.sources.stamped;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/core/status/report.ts"],"names":[],"mappings":"AA+EA,wEAAwE;AACxE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAU,CAAC;AAG3F;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,QAAQ;IACR,OAAO;IACP,cAAc;IACd,MAAM;IACN,WAAW;IACX,SAAS;IACT,UAAU;IACV,cAAc;CACN,CAAC;AAoQX,+FAA+F;AAC/F,MAAM,UAAU,YAAY,CAAC,CAAe;IAC1C,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,OAAO,CAAC,CAAe;IACrC,OAAO,CAAC,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;AACtD,CAAC","sourcesContent":["/**\n * Where does this work stand, and what is the next thing to do?\n *\n * Every other command answers a question you already know how to ask: validate\n * grades a target you name, delta briefs a service you name, verify checks a\n * feature you name. The one thing loam could not answer was the question an\n * agent asks FIRST — the one it has when it joins a repository halfway, or\n * comes back after losing its session. That knowledge existed, but only as\n * prose inside the slash commands (\"1. scaffold, 2. author, 3. validate\"),\n * which is to say it existed for whoever had read them recently and for nobody\n * else.\n *\n * The obvious implementation is a state file — a `.loam/state.json` recording\n * which step each feature reached. It is rejected here for the reason core/verify/\n * refuses a second mutable database: the moment a second record of the truth\n * exists, an author who edits a file by hand, a merge, a rebase or a\n * `git checkout` desynchronises it, and the state file's answer is confidently\n * wrong exactly when somebody trusts it. So `status` stores nothing and caches\n * nothing. It is a projection: every invocation re-derives the whole answer\n * from the files, and a file edited in another window is visible on the next\n * run with no invalidation step for anyone to forget.\n *\n * It also grades nothing itself. Every verdict here is somebody else's\n * function: `featureCoherence` (what `loam archive` refuses on) plus\n * `featureProvenance` and `requirementsMissingScenarios` (`loam validate\n * --feature`'s other error sources, as far as they exist as shared functions),\n * and `readVerificationState` + `featureChecklist`, the pair `loam verify`\n * uses. A second opinion about whether a feature holds together would be a\n * second answer to grade a pull request against, and the one a reader consults\n * is never the one the gate obeys (doctor.ts learnt this with its own config\n * validator). A check that lives inline inside `validate` cannot be inherited\n * at all, which is why `stage` never rests on `checks.coherent` alone.\n *\n * Which is why the rule this module is held to is one-directional: **status may\n * be more pessimistic than the gates, never greener.** `validate` and `archive`\n * answer two different questions — is the DOCUMENT valid, is the MERGE safe\n * (issue.ts) — and a projection that reported only one of them told an author\n * \"ship it\" about a feature the other one refuses. So `stage` and `next[]` take\n * the UNION: an error `validate` grades and a warning `archive` gates on both\n * make a feature `draft`, and test/status-agrees-with-gate.test.ts holds the\n * direction on a matrix of deliberately-damaged trees.\n *\n * ## The status vocabulary\n *\n * Five values, closed, and an agent is expected to branch on them:\n *\n * `missing` the artifact is owed and is not on disk; nothing stands in the\n * way of writing it now.\n * `blocked` the work is stalled on something that is NOT this artifact — an\n * artifact it is derived from is absent, or another feature in\n * flight has to archive first.\n * `draft` it is on disk and the shared checks report an error against it:\n * what exists is wrong.\n * `ready` it is on disk and clean, but its obligation is not discharged —\n * something outside the documents (code, a test run, a recording)\n * still has to answer it.\n * `done` nothing is owed here.\n *\n * The two pairs that look alike are the point of the set. `missing` and\n * `blocked` both mean \"not written\", and they are different instructions: one\n * says write it now, the other says you cannot yet and names what comes first —\n * an agent that cannot tell them apart authors a verification record against a\n * checklist that does not exist. `draft` and `ready` both mean \"on disk\", and\n * they are different instructions too: `draft` says the document is wrong and\n * the fix is in this repository, `ready` says the document is right and the\n * world has not caught up — the code is not built, or the claims are not\n * answered. Collapsing those two would send an agent to re-edit a spec that is\n * already correct.\n *\n * A value unreachable in a scope is simply never emitted there, deliberately:\n * the fleet form never says `draft`, because grading is a per-feature cost it\n * refuses to pay.\n */\nimport type { ServiceEntry } from \"../repo/entries.js\";\nimport type { UseCaseBlastRadius } from \"../usecases/touch.js\";\nimport type { AnsweredBy } from \"../verify/answers.js\";\nimport type { VerificationVerdict } from \"../verify/record.js\";\nimport type { Finding } from \"../vocabulary/report.js\";\n\n/** The closed set. The module header says what each value instructs. */\nexport const ARTIFACT_STATUSES = [\"missing\", \"blocked\", \"draft\", \"ready\", \"done\"] as const;\nexport type ArtifactStatus = (typeof ARTIFACT_STATUSES)[number];\n\n/**\n * The artifact kinds a feature can owe, in the order the cycle authors them.\n * `spec`, `arch-spec`, `openapi` and `asyncapi` are per-service and repeat\n * once per service the feature touches; `capabilities` repeats once per\n * capability document the feature's delta carries; the rest are feature-wide.\n *\n * `capabilities` sits where an analyst writes it — the business promise comes\n * before the services that keep it, and a capability-only feature is a real and\n * ordinary first commit.\n */\nexport const ARTIFACT_IDS = [\n \"intent\",\n \"delta\",\n \"capabilities\",\n \"spec\",\n \"arch-spec\",\n \"openapi\",\n \"asyncapi\",\n \"verification\",\n] as const;\nexport type ArtifactId = (typeof ARTIFACT_IDS)[number];\n\nexport interface ArtifactState {\n id: ArtifactId;\n /** The service this artifact belongs to, or null for the feature-wide ones. */\n service: string | null;\n /**\n * The capability this artifact belongs to, on `capabilities` rows only.\n *\n * A field of its own rather than a capability id put in `service`: the two\n * are different namespaces — a fleet may hold a service and a capability of\n * one name — and bending a published field to carry the other kind is how a\n * consumer grouping by service silently acquires a row that is not one. Absent\n * on every other row, so nothing that reads `service` today changes.\n */\n capability?: string;\n /** Repo-relative with forward slashes — diffable across machines, like every path in the contract. */\n path: string;\n exists: boolean;\n /**\n * Whether ABSENCE is a defect. False for the artifacts a feature may\n * legitimately not have: `delta.likec4` (a requirements-only change deletes\n * it), `arch.spec.md` and `asyncapi.yaml` (optional everywhere), and\n * `openapi.yaml` for a service\n * the living docs already carry. An absent optional artifact is `done` —\n * nothing is owed — and `exists: false` beside it is how a reader tells that\n * from a file that is present and fine.\n */\n required: boolean;\n status: ArtifactStatus;\n /** Artifact ids that must come first. Non-empty only when `status` is `blocked`. */\n blockedBy: string[];\n}\n\nexport interface NextStep {\n /** Stable — prose may change, this may not. */\n code: string;\n /** One sentence, actionable without reading any other field. */\n statement: string;\n /** The literal command to run. */\n command: string;\n artifact?: ArtifactId;\n service?: string;\n /** The repo-relative file the step is about, when it names one. */\n path?: string;\n}\n\nexport type NextActionKind = \"command\" | \"edit\" | \"external-repo\" | \"human-review\";\nexport type NextActionCwd = \"configured-repo\" | \"docs-repo\" | \"service-repo\";\n\n/**\n * The machine contract beside the legacy `NextStep.command` string.\n *\n * `command` exists only when every argument is concrete. An edit names the\n * file and puts the old command in `after`, because that command grades the\n * authored work rather than performing it. `needs` is data the caller must\n * obtain; it is never smuggled into a shell-looking placeholder.\n */\nexport interface NextExecution {\n kind: NextActionKind;\n runnable: boolean;\n cwd: NextActionCwd;\n command?: string;\n after?: string;\n needs?: string[];\n}\n\nexport type ExecutableNextStep = NextStep & { execution: NextExecution };\n\n/**\n * What the record beside the feature says, without asking whether anyone should\n * believe the code. `stale` is its own state and not merely \"recorded\": the\n * feature moved after the answers were written, so the record answers questions\n * that are no longer the ones being asked — which is exactly why `loam verify\n * --record` refuses to merge into it.\n */\nexport interface VerificationState {\n state: \"absent\" | \"unreadable\" | \"stale\" | \"recorded\";\n /** The day the record was written, when there is a readable one. */\n recorded: string | null;\n /**\n * `verify --json`'s own three-valued verdict, through `verificationVerdict` —\n * not a fourth opinion computed here. `attested` is the one that matters to a\n * reader of this command: complete, and resting on somebody's word about a\n * test rather than on a run.\n */\n verdict: VerificationVerdict;\n /** Every count below is `tallyRecord` — the record's `claims:` array, never its `summary:` block. */\n claims: number;\n confirmed: number;\n unconfirmed: number;\n /** Federated records only: claims nobody has answered yet. */\n unanswered: number;\n /** Of the confirmed, the scenario claims on an agent's word (`attestedClaims`). */\n attested: number;\n /**\n * Of the confirmed, one count per `answered_by` value (`confirmedProvenance`),\n * every key present — `list --json`'s `answered`, under the same name. A\n * different question from `attested`: that one asks only about the scenario\n * claims the verdict turns on, this one who answered EVERY kind, so a record\n * `verified` on its scenarios can still say six of its answers were an\n * agent's word about a permission or an edge.\n */\n answered: Record<AnsweredBy, number>;\n}\n\n/**\n * A `loam archive`/`unarchive` that was killed mid-commit, read off the intent\n * journal `.loam-commit` leaves in the docs repo.\n *\n * It belongs in a projection that grades nothing because it is not a grade: it\n * is the one repository state where every OTHER answer this command gives is\n * about files that may be half-written. `doctor` blocks on it; `status` is what\n * an agent runs first, and it reported `done` over a half-merged repo — so it\n * says so too, from the same journal, and leads its steps with the repair.\n *\n * Read, never repaired: recovery happens under the docs lock inside `archive`\n * and `unarchive`, and `status` writes nothing and takes no lock.\n */\nexport interface InterruptedCommit {\n /**\n * Which command was committing, or null when the journal cannot be read at\n * all. A sentence for a human, never a branch: version-2 journals carry any\n * writer's name (rebase, vouch, new, gherkin), so an enum here would be a\n * list this type has to chase.\n */\n command: string | null;\n feature: string | null;\n /** Who and when, so a human can place it against their own shell history. */\n host: string | null;\n pid: number | null;\n at: string | null;\n /** Docs-repo-relative paths that may hold half-written bytes. */\n files: string[];\n /** `.loam-commit` exists but does not parse — the worst case, because nothing can grade it. */\n unreadable: boolean;\n /** The command that recovers, or null when only a human can. */\n recover: string | null;\n}\n\nexport interface FeatureStatusReport {\n /**\n * A killed commit in this docs repo, or null. Present on both forms: it is a\n * fact about the repository, and every artifact row below it is a fact about\n * files that repository may have half-written.\n */\n interrupted: InterruptedCommit | null;\n feature: {\n id: string;\n dirName: string;\n path: string;\n archived: boolean;\n /** The feature's own rollup, from the same vocabulary as its artifacts. */\n stage: ArtifactStatus;\n /** Every service the feature touches, whatever `--service` narrowed the view to. */\n services: string[];\n /** Active features that must archive before this one, from the dependency index. */\n blockedBy: string[];\n };\n /** The `--service` narrowing, or null when the whole feature is in view. */\n service: string | null;\n artifacts: ArtifactState[];\n /**\n * The grade, from the same calls the two gates make: `featureCoherence`\n * (archive), plus `featureProvenance` and the scenario-coverage check — the\n * error sources `validate --feature` shares as functions. `ran` is false for\n * an archived feature: its delta is already merged into the living docs, so\n * re-grading it would compare a change against a world that has absorbed it.\n */\n checks: {\n ran: boolean;\n /**\n * No error among the findings above — `loam validate --feature`'s verdict\n * for every error source that is a shared function. It is not on its own\n * what makes a feature shippable: `stage` and `next[]` take the union with\n * archive's gating set, because a warning that gates is a warning this\n * field reports as coherent.\n */\n coherent: boolean;\n errors: number;\n warnings: number;\n /** How many findings would stop `loam archive` without `--approve`. */\n gating: number;\n /**\n * Findings, not Issues: the set is wider than coherence now, and `gates` is\n * resolved on every coherence finding so a consumer never re-implements the\n * severity default. One breach reported by `validate` and by `status` has\n * to arrive in one shape.\n */\n issues: Finding[];\n };\n verification: VerificationState;\n /**\n * The declared business flows the services IN VIEW are hops of — blast\n * radius, and the only field here that is a fact about the fleet rather than\n * about the feature.\n *\n * It narrows with `--service` exactly as `artifacts` and `next` do, because\n * `--service` is a lens on what the reader is being asked to do next. What it\n * must never do is go silently empty: `unreadable` is how it says that\n * `architecture/` could not be read, which is a different fact from a fleet\n * that draws no use cases and produces the identical empty list.\n *\n * The FLEET form has no equivalent, deliberately. That form grades nothing and\n * is coarse by design, and a per-feature flow join over every feature in flight\n * is exactly the per-feature cost it refuses to pay.\n */\n useCases: UseCaseBlastRadius;\n next: NextStep[];\n}\n\nexport interface FleetFeatureState {\n id: string;\n dirName: string;\n path: string;\n stage: ArtifactStatus;\n services: string[];\n blockedBy: string[];\n /** Artifacts the feature owes and has not written. Presence only — the fleet form grades nothing. */\n missing: string[];\n verification: VerificationState;\n}\n\nexport interface FleetStatusReport {\n /** See {@link FeatureStatusReport.interrupted}. */\n interrupted: InterruptedCommit | null;\n /**\n * Three disjoint buckets over `services/`, summing to `total`. Deliberately\n * coarser than `list`'s maturity ladder, and deliberately not the same words:\n * that ladder grades an adoption campaign rung by rung, this asks only\n * whether there is anything here to build a feature on.\n */\n services: {\n total: number;\n /** No `spec.md` — nothing about this service is written down. */\n undocumented: number;\n /** Documented, with no vouch stamp behind it. */\n draft: number;\n /** `status: verified` with a `sources_digest` — a person stood behind it. */\n vouched: number;\n /**\n * Of `vouched`, the ones read from a recorded SAMPLE rather than in full\n * (`vouch_scope`, either axis) — a SUBSET: the three above still sum to `total`.\n */\n sampledVouched: number;\n /**\n * Of `vouched`, the ones with a stamped document whose words moved after\n * the vouch (`content.stale`, either axis) — a SUBSET like\n * `sampledVouched`, and each one is a `next.revouch` step below.\n */\n contentStale: number;\n };\n features: FleetFeatureState[];\n /** Dependencies before consumers — which feature has to archive first. */\n order: string[];\n service: string | null;\n next: NextStep[];\n}\n\n/** Nothing is written down: no living spec at all. Every other absence is partial adoption. */\nexport function undocumented(s: ServiceEntry): boolean {\n return !s.has.spec;\n}\n\n/** A person stamped it — `loam vouch`'s exact postcondition, read back off the frontmatter. */\nexport function vouched(s: ServiceEntry): boolean {\n return s.status === \"verified\" && s.sources.stamped;\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { featureChecklist } from "../verify/checklist.js";
|
|
2
|
-
import { readVerificationState } from "../verify/file.js";
|
|
3
|
-
import { attestedNotice, tallyRecord, verificationVerdict } from "../verify/record.js";
|
|
2
|
+
import { answering, readVerificationState } from "../verify/read/file.js";
|
|
3
|
+
import { attestedNotice, confirmedProvenance, tallyRecord, verificationVerdict, } from "../verify/record.js";
|
|
4
4
|
/**
|
|
5
5
|
* Is the done-check discharged? `verificationVerdict`'s answer and nobody
|
|
6
6
|
* else's — status re-deriving it would be a fourth place the words "verified"
|
|
@@ -43,13 +43,13 @@ export function verificationStatus(v) {
|
|
|
43
43
|
* An archived feature's record is frozen history — archive merged its
|
|
44
44
|
* operations into the living contract, so a re-derived checklist can only
|
|
45
45
|
* mismatch, and calling it stale would slander every feature that shipped.
|
|
46
|
+
* What it CAN be held to is the checklist archive captured as it merged, and
|
|
47
|
+
* that is `grading.shipped`'s: a record answering an older checklist than the
|
|
48
|
+
* one the feature shipped with is stale, archived or not — the gap `gate
|
|
49
|
+
* --since` used to let through, since archiving a feature whose record had
|
|
50
|
+
* gone stale made it read `verified` again.
|
|
46
51
|
*/
|
|
47
|
-
export async function verificationState(docsDir, feature,
|
|
48
|
-
// The invocation's read index, when the caller holds one. It reaches the
|
|
49
|
-
// checklist derivation, whose enumerations and delta parse are memo hits
|
|
50
|
-
// through it — the difference between one fleet walk per invocation and one
|
|
51
|
-
// per feature for a caller grading several (`loam gate`'s check 3).
|
|
52
|
-
fleet) {
|
|
52
|
+
export async function verificationState(docsDir, feature, grading = {}) {
|
|
53
53
|
const empty = {
|
|
54
54
|
recorded: null,
|
|
55
55
|
verdict: "unverified",
|
|
@@ -58,6 +58,7 @@ fleet) {
|
|
|
58
58
|
unconfirmed: 0,
|
|
59
59
|
unanswered: 0,
|
|
60
60
|
attested: 0,
|
|
61
|
+
answered: { runner: 0, "external-runner": 0, agent: 0 },
|
|
61
62
|
};
|
|
62
63
|
const read = await readVerificationState(feature.dir);
|
|
63
64
|
if (read.state === "absent")
|
|
@@ -70,10 +71,16 @@ fleet) {
|
|
|
70
71
|
// unverified read here as "11 of 11 confirmed".
|
|
71
72
|
if (read.state === "unreadable")
|
|
72
73
|
return { state: { state: "unreadable", ...empty }, notice: null };
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
// The checklist the record is held to: the one asked now for an in-flight
|
|
75
|
+
// feature, the one captured at the merge for an archived one when the caller
|
|
76
|
+
// asks for that, none at all otherwise (frozen history). The files are then
|
|
77
|
+
// added up AGAINST it — the per-service files that answered it are the ones
|
|
78
|
+
// that count (`core/verify/read/union.ts`) — without reading them twice.
|
|
79
|
+
const against = feature.archived
|
|
80
|
+
? (grading.shipped ?? null)
|
|
81
|
+
: (await featureChecklist(docsDir, feature.dir, feature.id, grading.fleet)).digest;
|
|
82
|
+
const v = against === null ? read.verification : (answering(read.files, against) ?? read.verification);
|
|
83
|
+
const stale = against !== null && against !== v.checklist;
|
|
77
84
|
const tally = tallyRecord(v);
|
|
78
85
|
return {
|
|
79
86
|
state: {
|
|
@@ -85,6 +92,7 @@ fleet) {
|
|
|
85
92
|
unconfirmed: tally.unconfirmed,
|
|
86
93
|
unanswered: tally.unanswered,
|
|
87
94
|
attested: tally.attested,
|
|
95
|
+
answered: confirmedProvenance(v.claims),
|
|
88
96
|
},
|
|
89
97
|
notice: attestedNotice(v.claims, feature.id),
|
|
90
98
|
};
|
|
@@ -116,7 +124,10 @@ export function verifyStep(id, verification, view) {
|
|
|
116
124
|
if (verification.state === "unreadable") {
|
|
117
125
|
return step({
|
|
118
126
|
code: "next.verify",
|
|
119
|
-
|
|
127
|
+
// Never "or delete it": the record may be the one-file verification.yaml
|
|
128
|
+
// holding other services' attestations, and the command names the file
|
|
129
|
+
// that actually failed together with the repair that is safe for it.
|
|
130
|
+
statement: `${id}'s record at ${record.path} does not read as a record — the command names the file that failed and its safe repair; never delete a file other services' attestations may be in. Nothing will overwrite it while it is unreadable.`,
|
|
120
131
|
});
|
|
121
132
|
}
|
|
122
133
|
if (verification.state === "stale") {
|
|
@@ -158,8 +169,12 @@ export function verifyStep(id, verification, view) {
|
|
|
158
169
|
{
|
|
159
170
|
code: "next.verify-attested",
|
|
160
171
|
statement: `${verification.attested} of ${id}'s scenario claim(s) are confirmed on an agent's word, not on a test run ` +
|
|
161
|
-
`(verify.scenario-attested) — answer them from a
|
|
162
|
-
|
|
172
|
+
`(verify.scenario-attested) — answer them from a test report (a cucumber JSON report, or loam's runner-neutral ` +
|
|
173
|
+
`loamScenarioReport shape for any other runner), and the record stops resting on an assertion.`,
|
|
174
|
+
// The runner-neutral placeholder `verify.scenario-attested` itself
|
|
175
|
+
// suggests: a step that said "cucumber" alone told a fleet whose
|
|
176
|
+
// runner has no cucumber formatter that it had no route to `verified`.
|
|
177
|
+
command: `loam verify ${id} --results <report.json>`,
|
|
163
178
|
artifact: "verification",
|
|
164
179
|
path: record.path,
|
|
165
180
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verification.js","sourceRoot":"","sources":["../../../src/core/status/verification.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The verification record as `status` reads it: the rung the record stands on,\n * the projection of what it says, and the two `next[]` steps that discharge it\n * — where a test comes from, and what `loam verify` still wants answered.\n * Every verdict here is `core/verify/`'s own, re-read; nothing in this module is\n * a second definition of \"verified\".\n */\nimport type { FeatureEntry } from \"../repo/entries.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport { featureChecklist } from \"../verify/checklist.js\";\nimport { readVerificationState } from \"../verify/file.js\";\nimport { attestedNotice, tallyRecord, type VerificationVerdict, type VerifyNotice, verificationVerdict } from \"../verify/record.js\";\nimport type { ArtifactState, ArtifactStatus, NextStep, VerificationState } from \"./report.js\";\nimport type { DeltaScan } from \"./scan.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\n\n/**\n * Is the done-check discharged? `verificationVerdict`'s answer and nobody\n * else's — status re-deriving it would be a fourth place the words \"verified\"\n * are defined, and the day one of them changed (a scenario claim on an agent's\n * word stopped counting) the reader's copy would be the one still saying yes.\n * `attested` is deliberately NOT `done` here: it is complete work resting on an\n * assertion, which is what `ready` has always meant — the document is right and\n * the world has not caught up.\n */\nexport function fullyVerified(v: VerificationState): boolean {\n return v.verdict === \"verified\";\n}\n\n/**\n * The record's own rung. `stale` and `unreadable` are `draft` because both mean\n * the file on disk is the thing to fix; unconfirmed claims are `ready` because\n * the file is fine and the answer lives in the code.\n *\n * All four states are spelled, `recorded` included, so the `never` below fails\n * the build rather than the reader: a fifth record state added to\n * {@link VerificationState} would otherwise fall through into the last branch\n * and be graded by `fullyVerified` alone — silently answering `ready` about a\n * record whose new state nobody here has decided a rung for.\n */\nexport function verificationStatus(v: VerificationState): ArtifactStatus {\n if (v.state === \"absent\") return \"missing\";\n if (v.state === \"unreadable\" || v.state === \"stale\") return \"draft\";\n if (v.state === \"recorded\") return fullyVerified(v) ? \"done\" : \"ready\";\n const unreachable: never = v.state;\n throw new Error(`verificationStatus: no rung for verification state '${String(unreachable)}'`);\n}\n\n/**\n * Read the record and, only when there is one, ask whether it still answers\n * this feature. The checklist is derived on demand and never speculatively: it\n * loads the delta into a LikeC4 workspace, and paying that in order to print\n * \"no record\" is the cost `list` refused for its verification column.\n *\n * An archived feature's record is frozen history — archive merged its\n * operations into the living contract, so a re-derived checklist can only\n * mismatch, and calling it stale would slander every feature that shipped.\n */\nexport async function verificationState(\n docsDir: DocsDir,\n feature: FeatureEntry,\n // The invocation's read index, when the caller holds one. It reaches the\n // checklist derivation, whose enumerations and delta parse are memo hits\n // through it — the difference between one fleet walk per invocation and one\n // per feature for a caller grading several (`loam gate`'s check 3).\n fleet?: FleetContext,\n): Promise<{ state: VerificationState; notice: VerifyNotice | null }> {\n const empty = {\n recorded: null,\n verdict: \"unverified\" as VerificationVerdict,\n claims: 0,\n confirmed: 0,\n unconfirmed: 0,\n unanswered: 0,\n attested: 0,\n };\n const read = await readVerificationState(feature.dir);\n if (read.state === \"absent\") return { state: { state: \"absent\", ...empty }, notice: null };\n // A record whose summary contradicts its own claims is refused by the reader\n // itself (`verify.record-miscounted`), so this branch reports zeros rather\n // than salvaging counts from a file nobody can say which half of was edited.\n // Where there ARE counts they are `tallyRecord`'s, never the summary's — the\n // block used to be copied out verbatim, and a record `verify --json` calls\n // unverified read here as \"11 of 11 confirmed\".\n if (read.state === \"unreadable\") return { state: { state: \"unreadable\", ...empty }, notice: null };\n\n const v = read.verification;\n const stale = feature.archived\n ? false\n : (await featureChecklist(docsDir, feature.dir, feature.id, fleet)).digest !== v.checklist;\n const tally = tallyRecord(v);\n return {\n state: {\n state: stale ? \"stale\" : \"recorded\",\n recorded: v.recorded,\n verdict: verificationVerdict(tally, stale),\n claims: tally.claims,\n confirmed: tally.confirmed,\n unconfirmed: tally.unconfirmed,\n unanswered: tally.unanswered,\n attested: tally.attested,\n },\n notice: attestedNotice(v.claims, feature.id),\n };\n}\n\n/**\n * The done-check step, or none. Four different situations reach `loam verify`\n * and they are not the same work — nothing recorded, a file that will not read,\n * a record the feature has moved out from under, and claims still open — so the\n * statement says which. The code splits only where the WORK splits: starting a\n * verification and finishing one are different jobs, and an agent that treats a\n * stale record as a finished one ships a feature nobody checked.\n */\n/** The narrowed view a step is written for: what to name, and to whom. */\nexport interface StepView {\n artifacts?: ArtifactState[];\n services: string[];\n /** The service this repository is bound to, when a command is standing in one. */\n boundService: string | undefined;\n}\n\nexport function verifyStep(id: string, verification: VerificationState, view: StepView): NextStep[] {\n const { artifacts = [], services, boundService } = view;\n const record = artifacts.find((a) => a.id === \"verification\")!;\n const open = verification.unconfirmed + verification.unanswered;\n // The code is spelled at each call site as a `code:` property rather than\n // passed positionally, and the helper takes the whole pair: test/codes-drift.ts\n // collects the vocabulary by reading the source for that exact shape, so a\n // factory with a `code` PARAMETER would hide every code it emits from the one\n // guard that checks they are documented. Same DRY-ness, visible to the guard.\n const step = (s: { code: string; statement: string }): NextStep[] => [\n { ...s, command: `loam verify ${id} --json`, artifact: \"verification\", path: record.path },\n ];\n\n if (record.status === \"blocked\") {\n return step({\n code: \"next.verify\",\n statement: `${id} has nothing to derive a checklist from yet — write the delta and the requirement deltas first.`,\n });\n }\n if (verification.state === \"absent\") {\n return step({\n code: \"next.verify\",\n statement: `Nothing has been verified for ${id} — derive the checklist and answer every claim with evidence.`,\n });\n }\n if (verification.state === \"unreadable\") {\n return step({\n code: \"next.verify\",\n statement: `${record.path} exists but does not read as a record — repair or delete it; nothing will overwrite it while it is unreadable.`,\n });\n }\n if (verification.state === \"stale\") {\n return step({\n code: \"next.verify\",\n statement: `${id} moved after ${record.path} was written — the record answers a checklist that is no longer the one being asked.`,\n });\n }\n if (open > 0) {\n // The recording form only when this repository is one of the services that\n // owes an answer: `verify --record --service` binds the answers to THIS\n // repo's HEAD and refuses anywhere else, so handing a docs-repo reader that\n // command would name a step it cannot take.\n return boundService !== undefined && services.includes(boundService)\n ? [\n {\n code: \"next.attest-service\",\n statement:\n `${open} of ${verification.claims} claim(s) on ${id} are open, and this repository is '${boundService}' — ` +\n `answer the ones filed under it, bound to this commit; the rest belong to their own repositories.`,\n command: `loam verify ${id} --record answers.json --service ${boundService}`,\n artifact: \"verification\",\n service: boundService,\n path: record.path,\n },\n ]\n : step({\n code: \"next.verify-unconfirmed\",\n statement: `${open} of ${verification.claims} claim(s) on ${id} are not confirmed — close them from each affected service's own repository.`,\n });\n }\n if (verification.claims === 0) {\n return step({\n code: \"next.verify\",\n statement: `${record.path} answers no claims at all — nothing was asked, so nothing was checked; re-derive the checklist and see what this feature actually promises.`,\n });\n }\n if (verification.verdict === \"attested\") {\n return [\n {\n code: \"next.verify-attested\",\n statement:\n `${verification.attested} of ${id}'s scenario claim(s) are confirmed on an agent's word, not on a test run ` +\n `(verify.scenario-attested) — answer them from a cucumber report, and the record stops resting on an assertion.`,\n command: `loam verify ${id} --results <cucumber-report.json>`,\n artifact: \"verification\",\n path: record.path,\n },\n ];\n }\n return [];\n}\n\n/**\n * Where a test comes from. `loam gherkin` writes the .feature files whose green\n * run is the only thing that may answer a `scenario.tested` claim, and it runs\n * from the service's own repository — so the step exists wherever there are\n * scenarios to generate from, and names the repository to run it in when that\n * is not this one.\n */\nexport function testStep(\n id: string,\n verification: VerificationState,\n view: { services: string[]; scans: DeltaScan[]; boundService: string | undefined },\n): NextStep[] {\n const { services, scans, boundService } = view;\n if (fullyVerified(verification)) return [];\n const withScenarios = [\n ...new Set(scans.filter((s) => s.scenarios > 0 && services.includes(s.service)).map((s) => s.service)),\n ];\n if (withScenarios.length === 0) return [];\n const svc = boundService !== undefined && withScenarios.includes(boundService) ? boundService : withScenarios[0]!;\n const here = svc === boundService;\n return [\n {\n code: \"next.generate-tests\",\n statement:\n `${id}'s delta for ${svc} carries scenarios that no test run has answered yet — generate the suite, then implement against it` +\n (here\n ? \".\"\n : `. \\`loam gherkin\\` writes into the service's own repository, so run it from ${svc}'s${\n withScenarios.length > 1 ? ` (and likewise for ${withScenarios.slice(1).join(\", \")})` : \"\"\n }.`),\n command: `loam gherkin ${id} --service ${svc}`,\n artifact: \"spec\",\n service: svc,\n },\n ];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"verification.js","sourceRoot":"","sources":["../../../src/core/status/verification.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The verification record as `status` reads it: the rung the record stands on,\n * the projection of what it says, and the two `next[]` steps that discharge it\n * — where a test comes from, and what `loam verify` still wants answered.\n * Every verdict here is `core/verify/`'s own, re-read; nothing in this module is\n * a second definition of \"verified\".\n */\nimport type { FeatureEntry } from \"../repo/entries.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport { featureChecklist } from \"../verify/checklist.js\";\nimport { answering, readVerificationState } from \"../verify/read/file.js\";\nimport {\n attestedNotice,\n confirmedProvenance,\n tallyRecord,\n type VerificationVerdict,\n type VerifyNotice,\n verificationVerdict,\n} from \"../verify/record.js\";\nimport type { ArtifactState, ArtifactStatus, NextStep, VerificationState } from \"./report.js\";\nimport type { DeltaScan } from \"./scan.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\n\n/**\n * Is the done-check discharged? `verificationVerdict`'s answer and nobody\n * else's — status re-deriving it would be a fourth place the words \"verified\"\n * are defined, and the day one of them changed (a scenario claim on an agent's\n * word stopped counting) the reader's copy would be the one still saying yes.\n * `attested` is deliberately NOT `done` here: it is complete work resting on an\n * assertion, which is what `ready` has always meant — the document is right and\n * the world has not caught up.\n */\nexport function fullyVerified(v: VerificationState): boolean {\n return v.verdict === \"verified\";\n}\n\n/**\n * The record's own rung. `stale` and `unreadable` are `draft` because both mean\n * the file on disk is the thing to fix; unconfirmed claims are `ready` because\n * the file is fine and the answer lives in the code.\n *\n * All four states are spelled, `recorded` included, so the `never` below fails\n * the build rather than the reader: a fifth record state added to\n * {@link VerificationState} would otherwise fall through into the last branch\n * and be graded by `fullyVerified` alone — silently answering `ready` about a\n * record whose new state nobody here has decided a rung for.\n */\nexport function verificationStatus(v: VerificationState): ArtifactStatus {\n if (v.state === \"absent\") return \"missing\";\n if (v.state === \"unreadable\" || v.state === \"stale\") return \"draft\";\n if (v.state === \"recorded\") return fullyVerified(v) ? \"done\" : \"ready\";\n const unreachable: never = v.state;\n throw new Error(`verificationStatus: no rung for verification state '${String(unreachable)}'`);\n}\n\n/**\n * How a caller wants the record graded, beyond reading it.\n */\nexport interface StateGrading {\n /**\n * The invocation's read index, when the caller holds one. It reaches the\n * checklist derivation, whose enumerations and delta parse are memo hits\n * through it — the difference between one fleet walk per invocation and one\n * per feature for a caller grading several (`loam gate`'s check 3).\n */\n fleet?: FleetContext;\n /**\n * For an ARCHIVED feature only: the checklist digest `loam archive` captured\n * just before its merge (`readSnapshotChecklist`) — the question the feature\n * asked as it shipped, which a frozen record can still be graded against\n * without re-deriving anything. Only `loam gate --since` passes it, because\n * only the deploy question asks whether a record answered the feature that\n * SHIPPED; every other reader leaves it out and reads the record as frozen\n * history, as before. Null — an archive that captured none, by an older loam\n * or over a feature with no record then — is the frozen reading too.\n */\n shipped?: string | null;\n}\n\n/**\n * Read the record and, only when there is one, ask whether it still answers\n * this feature. The checklist is derived on demand and never speculatively: it\n * loads the delta into a LikeC4 workspace, and paying that in order to print\n * \"no record\" is the cost `list` refused for its verification column.\n *\n * An archived feature's record is frozen history — archive merged its\n * operations into the living contract, so a re-derived checklist can only\n * mismatch, and calling it stale would slander every feature that shipped.\n * What it CAN be held to is the checklist archive captured as it merged, and\n * that is `grading.shipped`'s: a record answering an older checklist than the\n * one the feature shipped with is stale, archived or not — the gap `gate\n * --since` used to let through, since archiving a feature whose record had\n * gone stale made it read `verified` again.\n */\nexport async function verificationState(\n docsDir: DocsDir,\n feature: FeatureEntry,\n grading: StateGrading = {},\n): Promise<{ state: VerificationState; notice: VerifyNotice | null }> {\n const empty = {\n recorded: null,\n verdict: \"unverified\" as VerificationVerdict,\n claims: 0,\n confirmed: 0,\n unconfirmed: 0,\n unanswered: 0,\n attested: 0,\n answered: { runner: 0, \"external-runner\": 0, agent: 0 },\n };\n const read = await readVerificationState(feature.dir);\n if (read.state === \"absent\") return { state: { state: \"absent\", ...empty }, notice: null };\n // A record whose summary contradicts its own claims is refused by the reader\n // itself (`verify.record-miscounted`), so this branch reports zeros rather\n // than salvaging counts from a file nobody can say which half of was edited.\n // Where there ARE counts they are `tallyRecord`'s, never the summary's — the\n // block used to be copied out verbatim, and a record `verify --json` calls\n // unverified read here as \"11 of 11 confirmed\".\n if (read.state === \"unreadable\") return { state: { state: \"unreadable\", ...empty }, notice: null };\n\n // The checklist the record is held to: the one asked now for an in-flight\n // feature, the one captured at the merge for an archived one when the caller\n // asks for that, none at all otherwise (frozen history). The files are then\n // added up AGAINST it — the per-service files that answered it are the ones\n // that count (`core/verify/read/union.ts`) — without reading them twice.\n const against = feature.archived\n ? (grading.shipped ?? null)\n : (await featureChecklist(docsDir, feature.dir, feature.id, grading.fleet)).digest;\n const v = against === null ? read.verification : (answering(read.files, against) ?? read.verification);\n const stale = against !== null && against !== v.checklist;\n const tally = tallyRecord(v);\n return {\n state: {\n state: stale ? \"stale\" : \"recorded\",\n recorded: v.recorded,\n verdict: verificationVerdict(tally, stale),\n claims: tally.claims,\n confirmed: tally.confirmed,\n unconfirmed: tally.unconfirmed,\n unanswered: tally.unanswered,\n attested: tally.attested,\n answered: confirmedProvenance(v.claims),\n },\n notice: attestedNotice(v.claims, feature.id),\n };\n}\n\n/**\n * The done-check step, or none. Four different situations reach `loam verify`\n * and they are not the same work — nothing recorded, a file that will not read,\n * a record the feature has moved out from under, and claims still open — so the\n * statement says which. The code splits only where the WORK splits: starting a\n * verification and finishing one are different jobs, and an agent that treats a\n * stale record as a finished one ships a feature nobody checked.\n */\n/** The narrowed view a step is written for: what to name, and to whom. */\nexport interface StepView {\n artifacts?: ArtifactState[];\n services: string[];\n /** The service this repository is bound to, when a command is standing in one. */\n boundService: string | undefined;\n}\n\nexport function verifyStep(id: string, verification: VerificationState, view: StepView): NextStep[] {\n const { artifacts = [], services, boundService } = view;\n const record = artifacts.find((a) => a.id === \"verification\")!;\n const open = verification.unconfirmed + verification.unanswered;\n // The code is spelled at each call site as a `code:` property rather than\n // passed positionally, and the helper takes the whole pair: test/codes-drift.ts\n // collects the vocabulary by reading the source for that exact shape, so a\n // factory with a `code` PARAMETER would hide every code it emits from the one\n // guard that checks they are documented. Same DRY-ness, visible to the guard.\n const step = (s: { code: string; statement: string }): NextStep[] => [\n { ...s, command: `loam verify ${id} --json`, artifact: \"verification\", path: record.path },\n ];\n\n if (record.status === \"blocked\") {\n return step({\n code: \"next.verify\",\n statement: `${id} has nothing to derive a checklist from yet — write the delta and the requirement deltas first.`,\n });\n }\n if (verification.state === \"absent\") {\n return step({\n code: \"next.verify\",\n statement: `Nothing has been verified for ${id} — derive the checklist and answer every claim with evidence.`,\n });\n }\n if (verification.state === \"unreadable\") {\n return step({\n code: \"next.verify\",\n // Never \"or delete it\": the record may be the one-file verification.yaml\n // holding other services' attestations, and the command names the file\n // that actually failed together with the repair that is safe for it.\n statement: `${id}'s record at ${record.path} does not read as a record — the command names the file that failed and its safe repair; never delete a file other services' attestations may be in. Nothing will overwrite it while it is unreadable.`,\n });\n }\n if (verification.state === \"stale\") {\n return step({\n code: \"next.verify\",\n statement: `${id} moved after ${record.path} was written — the record answers a checklist that is no longer the one being asked.`,\n });\n }\n if (open > 0) {\n // The recording form only when this repository is one of the services that\n // owes an answer: `verify --record --service` binds the answers to THIS\n // repo's HEAD and refuses anywhere else, so handing a docs-repo reader that\n // command would name a step it cannot take.\n return boundService !== undefined && services.includes(boundService)\n ? [\n {\n code: \"next.attest-service\",\n statement:\n `${open} of ${verification.claims} claim(s) on ${id} are open, and this repository is '${boundService}' — ` +\n `answer the ones filed under it, bound to this commit; the rest belong to their own repositories.`,\n command: `loam verify ${id} --record answers.json --service ${boundService}`,\n artifact: \"verification\",\n service: boundService,\n path: record.path,\n },\n ]\n : step({\n code: \"next.verify-unconfirmed\",\n statement: `${open} of ${verification.claims} claim(s) on ${id} are not confirmed — close them from each affected service's own repository.`,\n });\n }\n if (verification.claims === 0) {\n return step({\n code: \"next.verify\",\n statement: `${record.path} answers no claims at all — nothing was asked, so nothing was checked; re-derive the checklist and see what this feature actually promises.`,\n });\n }\n if (verification.verdict === \"attested\") {\n return [\n {\n code: \"next.verify-attested\",\n statement:\n `${verification.attested} of ${id}'s scenario claim(s) are confirmed on an agent's word, not on a test run ` +\n `(verify.scenario-attested) — answer them from a test report (a cucumber JSON report, or loam's runner-neutral ` +\n `loamScenarioReport shape for any other runner), and the record stops resting on an assertion.`,\n // The runner-neutral placeholder `verify.scenario-attested` itself\n // suggests: a step that said \"cucumber\" alone told a fleet whose\n // runner has no cucumber formatter that it had no route to `verified`.\n command: `loam verify ${id} --results <report.json>`,\n artifact: \"verification\",\n path: record.path,\n },\n ];\n }\n return [];\n}\n\n/**\n * Where a test comes from. `loam gherkin` writes the .feature files whose green\n * run is the only thing that may answer a `scenario.tested` claim, and it runs\n * from the service's own repository — so the step exists wherever there are\n * scenarios to generate from, and names the repository to run it in when that\n * is not this one.\n */\nexport function testStep(\n id: string,\n verification: VerificationState,\n view: { services: string[]; scans: DeltaScan[]; boundService: string | undefined },\n): NextStep[] {\n const { services, scans, boundService } = view;\n if (fullyVerified(verification)) return [];\n const withScenarios = [\n ...new Set(scans.filter((s) => s.scenarios > 0 && services.includes(s.service)).map((s) => s.service)),\n ];\n if (withScenarios.length === 0) return [];\n const svc = boundService !== undefined && withScenarios.includes(boundService) ? boundService : withScenarios[0]!;\n const here = svc === boundService;\n return [\n {\n code: \"next.generate-tests\",\n statement:\n `${id}'s delta for ${svc} carries scenarios that no test run has answered yet — generate the suite, then implement against it` +\n (here\n ? \".\"\n : `. \\`loam gherkin\\` writes into the service's own repository, so run it from ${svc}'s${\n withScenarios.length > 1 ? ` (and likewise for ${withScenarios.slice(1).join(\", \")})` : \"\"\n }.`),\n command: `loam gherkin ${id} --service ${svc}`,\n artifact: \"spec\",\n service: svc,\n },\n ];\n}\n"]}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The work packet: one feature's unit-of-work list, derived and never stored.
|
|
3
|
+
*
|
|
4
|
+
* It is a READ. Nothing here writes, nothing caches, and there is no state
|
|
5
|
+
* anywhere that says a unit is finished — done is "the unit no longer appears",
|
|
6
|
+
* which is what makes the list impossible to falsify by editing a checkbox. The
|
|
7
|
+
* inputs are the same documents `loam verify` derives its questions from
|
|
8
|
+
* (`core/verify/checklist.ts`), the same delta scans `loam status` reads, the
|
|
9
|
+
* record beside the feature, and — only when standing in the bound service repo
|
|
10
|
+
* — the stamps in the generated suite.
|
|
11
|
+
*
|
|
12
|
+
* Everything the packet reports is therefore re-derivable from the tree by
|
|
13
|
+
* anybody holding it, which is the property an agent orchestrating other agents
|
|
14
|
+
* actually needs: it can ask again and compare, rather than trusting a report.
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync } from "node:fs";
|
|
17
|
+
import { readFile } from "node:fs/promises";
|
|
18
|
+
import { stripFrontmatter } from "../document/frontmatter.js";
|
|
19
|
+
import { repoPath } from "../envelope/json.js";
|
|
20
|
+
import { FleetContext } from "../fleet-context.js";
|
|
21
|
+
import { compareIds } from "../repo/entries.js";
|
|
22
|
+
import { featurePaths } from "../repo/paths.js";
|
|
23
|
+
import { featureChecklist } from "../verify/checklist.js";
|
|
24
|
+
import { readRecordView, settledClaims } from "./record.js";
|
|
25
|
+
import { readStamps } from "./stamps.js";
|
|
26
|
+
import { axisSize, describeUnits, readContracts, readDeltaAxes, scenarioSources, } from "./sources.js";
|
|
27
|
+
import { doneCheck, FRONTIER_LIMIT, isOpen, linkDependencies, sortUnits } from "./unit.js";
|
|
28
|
+
/** The step a claim-backed unit is waiting at. */
|
|
29
|
+
function stepFor(claim, stamps) {
|
|
30
|
+
if (claim.kind !== "scenario.tested")
|
|
31
|
+
return "implement";
|
|
32
|
+
// Only this repository's suite may be graded. Another service's tests live in
|
|
33
|
+
// another repository, and reporting `generate` for them would be a claim about
|
|
34
|
+
// a directory this run has never seen.
|
|
35
|
+
if (stamps.state !== "read" || stamps.service !== claim.subject)
|
|
36
|
+
return "implement";
|
|
37
|
+
return claim.digest !== undefined && stamps.digests.has(claim.digest) ? "implement" : "generate";
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The claim-backed units: every claim the record has not closed.
|
|
41
|
+
*
|
|
42
|
+
* The claim's own fields ride through verbatim — the task id IS the claim id,
|
|
43
|
+
* so a unit can be handed straight back to `loam verify` and two runs of
|
|
44
|
+
* `loam tasks` are diffable exactly as two checklists are.
|
|
45
|
+
*/
|
|
46
|
+
function claimUnits(claims, settled, stamps, sources) {
|
|
47
|
+
const out = [];
|
|
48
|
+
for (const claim of claims) {
|
|
49
|
+
if (settled.done.has(claim.id))
|
|
50
|
+
continue;
|
|
51
|
+
const source = sources.get(claim.id);
|
|
52
|
+
out.push({
|
|
53
|
+
id: claim.id,
|
|
54
|
+
kind: claim.kind,
|
|
55
|
+
subject: claim.subject,
|
|
56
|
+
at: settled.attested.has(claim.id) ? "attested" : stepFor(claim, stamps),
|
|
57
|
+
claim: claim.claim,
|
|
58
|
+
...(claim.digest === undefined ? {} : { digest: claim.digest }),
|
|
59
|
+
...(claim.operation === undefined ? {} : { operation: claim.operation }),
|
|
60
|
+
...(claim.token === undefined ? {} : { token: claim.token }),
|
|
61
|
+
dependsOn: [],
|
|
62
|
+
// The digest cross-check is the belt on the id join (`./sources.ts`): a
|
|
63
|
+
// source whose words hash to something else is somebody ELSE's
|
|
64
|
+
// Given/When/Then, and no source at all is the honest answer.
|
|
65
|
+
...(source !== undefined && source.digest === claim.digest ? { source: source.source } : {}),
|
|
66
|
+
done: { check: doneCheck(claim) },
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The four standing review instructions, computed with this feature's own ids.
|
|
73
|
+
*
|
|
74
|
+
* They are on the payload rather than in the docs because the reviewer of a
|
|
75
|
+
* packet is usually another agent, and an agent that has to go fetch a protocol
|
|
76
|
+
* to learn how to check the work is an agent that will not. Each one names a
|
|
77
|
+
* failure that has actually happened: trusting a checkmark instead of
|
|
78
|
+
* re-deriving, taking one answer set on faith, hand-editing a generated suite
|
|
79
|
+
* (whose digests are what `--results` matches on), and implementing one half of
|
|
80
|
+
* a contract.
|
|
81
|
+
*/
|
|
82
|
+
function reviewNotes(feature, gherkinDir) {
|
|
83
|
+
return [
|
|
84
|
+
`re-derive, do not trust checkmarks: loam tasks ${feature} --json and compare open units against the implementer's claim`,
|
|
85
|
+
`cross-examine recorded answers: loam verify ${feature} --diff-answers <a.json> <b.json>`,
|
|
86
|
+
`generated suite is read-only: no edits under ${gherkinDir}/loam/, and step definitions must not paraphrase scenarios`,
|
|
87
|
+
"honour the contract in both directions: every expose[] operationId exists under exactly that id; every call[] op is actually called",
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
/** The operations this lens promises to expose, and the ones it promises to call. */
|
|
91
|
+
function contractTokens(claims, lens) {
|
|
92
|
+
const scoped = claims.filter((claim) => lens === null || claim.subject === lens);
|
|
93
|
+
const pick = (kind, field) => [...new Set(scoped.filter((c) => c.kind === kind).map((c) => c[field]).filter((v) => v !== undefined))].sort(compareIds);
|
|
94
|
+
return { expose: pick("api.exposes", "operation"), call: pick("c4.calls", "token") };
|
|
95
|
+
}
|
|
96
|
+
export async function workPacket(request) {
|
|
97
|
+
const { docsDir, feature, lens, binding } = request;
|
|
98
|
+
// One read index for the whole derivation: the checklist, the delta axes and
|
|
99
|
+
// the architecture parse all reach the same documents, and without a shared
|
|
100
|
+
// context each would be its own fleet walk.
|
|
101
|
+
const context = new FleetContext();
|
|
102
|
+
const checklist = await featureChecklist(docsDir, feature.dir, feature.id, context);
|
|
103
|
+
const view = await readRecordView(feature.dir, checklist);
|
|
104
|
+
const settled = settledClaims(view);
|
|
105
|
+
const axes = await readDeltaAxes(docsDir, feature.dir, feature.services, context);
|
|
106
|
+
const stamps = await readStamps(binding);
|
|
107
|
+
const contracts = await readContracts(docsDir, feature.dir, feature.services, context);
|
|
108
|
+
const all = sortUnits(withDependencies([
|
|
109
|
+
...describeUnits(feature.id, axes),
|
|
110
|
+
...claimUnits(checklist.claims, settled, stamps, scenarioSources(feature.id, axes)),
|
|
111
|
+
]));
|
|
112
|
+
const units = lens === null ? all : all.filter((unit) => unit.subject === lens);
|
|
113
|
+
const open = units.filter(isOpen);
|
|
114
|
+
const paths = featurePaths(feature.dir);
|
|
115
|
+
const intent = existsSync(paths.intent)
|
|
116
|
+
? stripFrontmatter(await readFile(paths.intent, "utf8")).trim()
|
|
117
|
+
: null;
|
|
118
|
+
return {
|
|
119
|
+
feature: feature.id,
|
|
120
|
+
service: lens,
|
|
121
|
+
services: [...new Set([...feature.services, ...checklist.claims.map((c) => c.subject)])].sort(compareIds),
|
|
122
|
+
path: repoPath(docsDir, feature.dir),
|
|
123
|
+
checklist: checklist.digest,
|
|
124
|
+
record: view.state,
|
|
125
|
+
stamps: stamps.state,
|
|
126
|
+
intent,
|
|
127
|
+
size: { ...axisSize(axes), operations: contracts.operations, messages: contracts.messages, calls: countCalls(checklist.claims, lens) },
|
|
128
|
+
units,
|
|
129
|
+
counts: {
|
|
130
|
+
open: open.length,
|
|
131
|
+
done: checklist.claims.filter((c) => settled.done.has(c.id) && (lens === null || c.subject === lens)).length,
|
|
132
|
+
attested: units.filter((unit) => unit.at === "attested").length,
|
|
133
|
+
},
|
|
134
|
+
frontier: open.slice(0, FRONTIER_LIMIT).map((unit) => unit.id),
|
|
135
|
+
frontierElided: Math.max(0, open.length - FRONTIER_LIMIT),
|
|
136
|
+
...(lens === null ? {} : { waitingOn: waitingOn(units, all, lens) }),
|
|
137
|
+
contracts: contractTokens(checklist.claims, lens),
|
|
138
|
+
review: reviewNotes(feature.id, binding.gherkinDir ?? "features"),
|
|
139
|
+
unreadable: contracts.unreadable,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/** `linkDependencies` mutates in place; this is the expression form the assembly above reads in. */
|
|
143
|
+
function withDependencies(units) {
|
|
144
|
+
linkDependencies(units);
|
|
145
|
+
return units;
|
|
146
|
+
}
|
|
147
|
+
/** How many call edges this lens carries — the c4.calls claims, answered or not. */
|
|
148
|
+
function countCalls(claims, lens) {
|
|
149
|
+
return claims.filter((claim) => claim.kind === "c4.calls" && (lens === null || claim.subject === lens)).length;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* What this service is waiting on somebody else for.
|
|
153
|
+
*
|
|
154
|
+
* Under a lens the dependency edges point out of the view, and a consumer that
|
|
155
|
+
* could not see where would read `dependsOn` as a dangling id. These are the
|
|
156
|
+
* units on the other end: OPEN, in another service, and named with enough text
|
|
157
|
+
* that the reader knows which repository to go ask. Attested units are left out
|
|
158
|
+
* — they are complete work, and nobody is blocked on somebody else's
|
|
159
|
+
* outstanding cucumber run.
|
|
160
|
+
*/
|
|
161
|
+
function waitingOn(units, all, lens) {
|
|
162
|
+
const byId = new Map(all.map((unit) => [unit.id, unit]));
|
|
163
|
+
const out = new Map();
|
|
164
|
+
for (const unit of units) {
|
|
165
|
+
for (const id of unit.dependsOn) {
|
|
166
|
+
const provider = byId.get(id);
|
|
167
|
+
if (provider === undefined || provider.subject === lens || !isOpen(provider))
|
|
168
|
+
continue;
|
|
169
|
+
out.set(id, { id, subject: provider.subject, kind: provider.kind, claim: provider.claim });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return [...out.values()].sort((a, b) => compareIds(a.id, b.id));
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=packet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packet.js","sourceRoot":"","sources":["../../../src/core/tasks/packet.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The work packet: one feature's unit-of-work list, derived and never stored.\n *\n * It is a READ. Nothing here writes, nothing caches, and there is no state\n * anywhere that says a unit is finished — done is \"the unit no longer appears\",\n * which is what makes the list impossible to falsify by editing a checkbox. The\n * inputs are the same documents `loam verify` derives its questions from\n * (`core/verify/checklist.ts`), the same delta scans `loam status` reads, the\n * record beside the feature, and — only when standing in the bound service repo\n * — the stamps in the generated suite.\n *\n * Everything the packet reports is therefore re-derivable from the tree by\n * anybody holding it, which is the property an agent orchestrating other agents\n * actually needs: it can ask again and compare, rather than trusting a report.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { stripFrontmatter } from \"../document/frontmatter.js\";\nimport { repoPath } from \"../envelope/json.js\";\nimport { FleetContext } from \"../fleet-context.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\nimport { compareIds, type FeatureEntry } from \"../repo/entries.js\";\nimport { featurePaths } from \"../repo/paths.js\";\nimport { featureChecklist, type Claim } from \"../verify/checklist.js\";\nimport { readRecordView, settledClaims, type RecordState } from \"./record.js\";\nimport { readStamps, type GherkinBinding, type StampReading } from \"./stamps.js\";\nimport {\n axisSize,\n describeUnits,\n readContracts,\n readDeltaAxes,\n scenarioSources,\n type DeltaSize,\n type MintedSource,\n type Unreadable,\n} from \"./sources.js\";\nimport { doneCheck, FRONTIER_LIMIT, isOpen, linkDependencies, sortUnits, type TaskStep, type TaskUnit } from \"./unit.js\";\n\nexport interface PacketRequest {\n docsDir: DocsDir;\n /** Active only — an archived feature is refused by the caller, and why is stated there. */\n feature: FeatureEntry;\n /** The `--service` lens, or null for the whole feature. A lens, never a binding check. */\n lens: string | null;\n binding: GherkinBinding;\n}\n\n/** An open unit in another service that one of this lens's units is waiting on. */\nexport interface WaitingOn {\n id: string;\n subject: string;\n kind: string;\n claim: string;\n}\n\nexport interface WorkPacket {\n feature: string;\n service: string | null;\n services: string[];\n path: string;\n /** The checklist digest — how a consumer detects that the question set itself moved. */\n checklist: string;\n record: RecordState;\n stamps: \"read\" | \"unknown\";\n intent: string | null;\n /**\n * The size of the DELTA, not of the open work: how much this feature says,\n * whatever has been answered. A packet that shrank as work closed would make\n * \"this feature is small\" and \"this feature is nearly done\" the same sentence.\n */\n size: DeltaSize;\n units: TaskUnit[];\n counts: { open: number; done: number; attested: number };\n frontier: string[];\n frontierElided: number;\n waitingOn?: WaitingOn[];\n contracts: { expose: string[]; call: string[] };\n review: string[];\n unreadable: Unreadable;\n}\n\n/** The step a claim-backed unit is waiting at. */\nfunction stepFor(claim: Claim, stamps: StampReading): TaskStep {\n if (claim.kind !== \"scenario.tested\") return \"implement\";\n // Only this repository's suite may be graded. Another service's tests live in\n // another repository, and reporting `generate` for them would be a claim about\n // a directory this run has never seen.\n if (stamps.state !== \"read\" || stamps.service !== claim.subject) return \"implement\";\n return claim.digest !== undefined && stamps.digests.has(claim.digest) ? \"implement\" : \"generate\";\n}\n\n/**\n * The claim-backed units: every claim the record has not closed.\n *\n * The claim's own fields ride through verbatim — the task id IS the claim id,\n * so a unit can be handed straight back to `loam verify` and two runs of\n * `loam tasks` are diffable exactly as two checklists are.\n */\nfunction claimUnits(\n claims: readonly Claim[],\n settled: { done: Set<string>; attested: Set<string> },\n stamps: StampReading,\n sources: ReadonlyMap<string, MintedSource>,\n): TaskUnit[] {\n const out: TaskUnit[] = [];\n for (const claim of claims) {\n if (settled.done.has(claim.id)) continue;\n const source = sources.get(claim.id);\n out.push({\n id: claim.id,\n kind: claim.kind,\n subject: claim.subject,\n at: settled.attested.has(claim.id) ? \"attested\" : stepFor(claim, stamps),\n claim: claim.claim,\n ...(claim.digest === undefined ? {} : { digest: claim.digest }),\n ...(claim.operation === undefined ? {} : { operation: claim.operation }),\n ...(claim.token === undefined ? {} : { token: claim.token }),\n dependsOn: [],\n // The digest cross-check is the belt on the id join (`./sources.ts`): a\n // source whose words hash to something else is somebody ELSE's\n // Given/When/Then, and no source at all is the honest answer.\n ...(source !== undefined && source.digest === claim.digest ? { source: source.source } : {}),\n done: { check: doneCheck(claim) },\n });\n }\n return out;\n}\n\n/**\n * The four standing review instructions, computed with this feature's own ids.\n *\n * They are on the payload rather than in the docs because the reviewer of a\n * packet is usually another agent, and an agent that has to go fetch a protocol\n * to learn how to check the work is an agent that will not. Each one names a\n * failure that has actually happened: trusting a checkmark instead of\n * re-deriving, taking one answer set on faith, hand-editing a generated suite\n * (whose digests are what `--results` matches on), and implementing one half of\n * a contract.\n */\nfunction reviewNotes(feature: string, gherkinDir: string): string[] {\n return [\n `re-derive, do not trust checkmarks: loam tasks ${feature} --json and compare open units against the implementer's claim`,\n `cross-examine recorded answers: loam verify ${feature} --diff-answers <a.json> <b.json>`,\n `generated suite is read-only: no edits under ${gherkinDir}/loam/, and step definitions must not paraphrase scenarios`,\n \"honour the contract in both directions: every expose[] operationId exists under exactly that id; every call[] op is actually called\",\n ];\n}\n\n/** The operations this lens promises to expose, and the ones it promises to call. */\nfunction contractTokens(claims: readonly Claim[], lens: string | null): { expose: string[]; call: string[] } {\n const scoped = claims.filter((claim) => lens === null || claim.subject === lens);\n const pick = (kind: string, field: \"operation\" | \"token\"): string[] =>\n [...new Set(scoped.filter((c) => c.kind === kind).map((c) => c[field]).filter((v): v is string => v !== undefined))].sort(\n compareIds,\n );\n return { expose: pick(\"api.exposes\", \"operation\"), call: pick(\"c4.calls\", \"token\") };\n}\n\nexport async function workPacket(request: PacketRequest): Promise<WorkPacket> {\n const { docsDir, feature, lens, binding } = request;\n // One read index for the whole derivation: the checklist, the delta axes and\n // the architecture parse all reach the same documents, and without a shared\n // context each would be its own fleet walk.\n const context = new FleetContext();\n const checklist = await featureChecklist(docsDir, feature.dir, feature.id, context);\n const view = await readRecordView(feature.dir, checklist);\n const settled = settledClaims(view);\n const axes = await readDeltaAxes(docsDir, feature.dir, feature.services, context);\n const stamps = await readStamps(binding);\n const contracts = await readContracts(docsDir, feature.dir, feature.services, context);\n\n const all = sortUnits(withDependencies([\n ...describeUnits(feature.id, axes),\n ...claimUnits(checklist.claims, settled, stamps, scenarioSources(feature.id, axes)),\n ]));\n const units = lens === null ? all : all.filter((unit) => unit.subject === lens);\n const open = units.filter(isOpen);\n\n const paths = featurePaths(feature.dir);\n const intent = existsSync(paths.intent)\n ? stripFrontmatter(await readFile(paths.intent, \"utf8\")).trim()\n : null;\n\n return {\n feature: feature.id,\n service: lens,\n services: [...new Set([...feature.services, ...checklist.claims.map((c) => c.subject)])].sort(compareIds),\n path: repoPath(docsDir, feature.dir),\n checklist: checklist.digest,\n record: view.state,\n stamps: stamps.state,\n intent,\n size: { ...axisSize(axes), operations: contracts.operations, messages: contracts.messages, calls: countCalls(checklist.claims, lens) },\n units,\n counts: {\n open: open.length,\n done: checklist.claims.filter((c) => settled.done.has(c.id) && (lens === null || c.subject === lens)).length,\n attested: units.filter((unit) => unit.at === \"attested\").length,\n },\n frontier: open.slice(0, FRONTIER_LIMIT).map((unit) => unit.id),\n frontierElided: Math.max(0, open.length - FRONTIER_LIMIT),\n ...(lens === null ? {} : { waitingOn: waitingOn(units, all, lens) }),\n contracts: contractTokens(checklist.claims, lens),\n review: reviewNotes(feature.id, binding.gherkinDir ?? \"features\"),\n unreadable: contracts.unreadable,\n };\n}\n\n/** `linkDependencies` mutates in place; this is the expression form the assembly above reads in. */\nfunction withDependencies(units: TaskUnit[]): TaskUnit[] {\n linkDependencies(units);\n return units;\n}\n\n/** How many call edges this lens carries — the c4.calls claims, answered or not. */\nfunction countCalls(claims: readonly Claim[], lens: string | null): number {\n return claims.filter((claim) => claim.kind === \"c4.calls\" && (lens === null || claim.subject === lens)).length;\n}\n\n/**\n * What this service is waiting on somebody else for.\n *\n * Under a lens the dependency edges point out of the view, and a consumer that\n * could not see where would read `dependsOn` as a dangling id. These are the\n * units on the other end: OPEN, in another service, and named with enough text\n * that the reader knows which repository to go ask. Attested units are left out\n * — they are complete work, and nobody is blocked on somebody else's\n * outstanding cucumber run.\n */\nfunction waitingOn(units: readonly TaskUnit[], all: readonly TaskUnit[], lens: string): WaitingOn[] {\n const byId = new Map(all.map((unit) => [unit.id, unit]));\n const out = new Map<string, WaitingOn>();\n for (const unit of units) {\n for (const id of unit.dependsOn) {\n const provider = byId.get(id);\n if (provider === undefined || provider.subject === lens || !isOpen(provider)) continue;\n out.set(id, { id, subject: provider.subject, kind: provider.kind, claim: provider.claim });\n }\n }\n return [...out.values()].sort((a, b) => compareIds(a.id, b.id));\n}\n"]}
|