@ybotok/loam 0.2.0-alpha.6 → 0.2.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1477 -1
- package/COMPARISON.md +91 -3
- package/CONTRIBUTING.md +37 -0
- package/MIGRATING-from-OpenSpec.md +40 -22
- package/QUICKSTART.md +181 -9
- package/README.md +256 -57
- package/ROADMAP.md +108 -11
- package/SCHEMA.md +721 -130
- package/WORKFLOW.md +272 -62
- package/dist/cli.js +26 -8
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +5 -10
- package/dist/commands/adopt/adopt.js.map +1 -1
- package/dist/commands/archive/archive.js +15 -18
- package/dist/commands/archive/archive.js.map +1 -1
- package/dist/commands/archive/plan/contracts/asyncapi.js +27 -41
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/consumers.js +56 -0
- package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +25 -42
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/verdict-lines.js +41 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +3 -7
- package/dist/commands/archive/plan/gate.js.map +1 -1
- package/dist/commands/archive/plan/landscape.js +4 -4
- package/dist/commands/archive/plan/landscape.js.map +1 -1
- package/dist/commands/archive/plan/refusal.js +8 -18
- package/dist/commands/archive/plan/refusal.js.map +1 -1
- package/dist/commands/archive/plan/specs.js +134 -25
- package/dist/commands/archive/plan/specs.js.map +1 -1
- package/dist/commands/archive/plan/state.js +1 -0
- package/dist/commands/archive/plan/state.js.map +1 -1
- package/dist/commands/archive/run.js +82 -33
- package/dist/commands/archive/run.js.map +1 -1
- package/dist/commands/archive/shipped.js +49 -0
- package/dist/commands/archive/shipped.js.map +1 -0
- package/dist/commands/context/context.js +4 -7
- package/dist/commands/context/context.js.map +1 -1
- package/dist/commands/context/print.js +2 -2
- package/dist/commands/context/print.js.map +1 -1
- package/dist/commands/delta/delta.js +44 -12
- package/dist/commands/delta/delta.js.map +1 -1
- package/dist/commands/delta/print.js +1 -1
- package/dist/commands/delta/print.js.map +1 -1
- package/dist/commands/dependencies.js +3 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +20 -27
- package/dist/commands/diff/diff.js.map +1 -1
- package/dist/commands/explore.js +4 -8
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +45 -8
- package/dist/commands/gate/gate.js.map +1 -1
- package/dist/commands/gate/print.js +15 -3
- package/dist/commands/gate/print.js.map +1 -1
- package/dist/commands/gherkin/commit.js +13 -54
- package/dist/commands/gherkin/commit.js.map +1 -1
- package/dist/commands/gherkin/gherkin.js +36 -20
- package/dist/commands/gherkin/gherkin.js.map +1 -1
- package/dist/commands/gherkin/reconcile.js +10 -13
- package/dist/commands/gherkin/reconcile.js.map +1 -1
- package/dist/commands/init/first-hour.js +44 -0
- package/dist/commands/init/first-hour.js.map +1 -1
- package/dist/commands/init/init.js +18 -22
- package/dist/commands/init/init.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +4 -6
- package/dist/commands/list/campaign/campaign.js.map +1 -1
- package/dist/commands/list/json.js +0 -4
- package/dist/commands/list/json.js.map +1 -1
- package/dist/commands/list/list.js +38 -23
- package/dist/commands/list/list.js.map +1 -1
- package/dist/commands/list/print.js +38 -6
- package/dist/commands/list/print.js.map +1 -1
- package/dist/commands/list/suites/suites.js +140 -0
- package/dist/commands/list/suites/suites.js.map +1 -0
- package/dist/commands/list/views.js +28 -33
- package/dist/commands/list/views.js.map +1 -1
- package/dist/commands/mcp/dispatch.js +91 -36
- package/dist/commands/mcp/dispatch.js.map +1 -1
- package/dist/commands/mcp/mcp.js +56 -15
- package/dist/commands/mcp/mcp.js.map +1 -1
- package/dist/commands/mcp/serve.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/active.js +27 -22
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/target.js +2 -2
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/error.js +7 -8
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
- package/dist/commands/new/capability-template.js +118 -0
- package/dist/commands/new/capability-template.js.map +1 -0
- package/dist/commands/new/commit.js +8 -22
- package/dist/commands/new/commit.js.map +1 -1
- package/dist/commands/new/modifies.js +206 -0
- package/dist/commands/new/modifies.js.map +1 -0
- package/dist/commands/new/new.js +59 -14
- package/dist/commands/new/new.js.map +1 -1
- package/dist/commands/new/templates.js +48 -124
- package/dist/commands/new/templates.js.map +1 -1
- package/dist/commands/open.js +3 -7
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/policy/gate.js +27 -0
- package/dist/commands/policy/gate.js.map +1 -1
- package/dist/commands/rebase/plan.js +2 -2
- package/dist/commands/rebase/plan.js.map +1 -1
- package/dist/commands/rebase/rebase.js +60 -35
- package/dist/commands/rebase/rebase.js.map +1 -1
- package/dist/commands/seed/commit.js +55 -26
- package/dist/commands/seed/commit.js.map +1 -1
- package/dist/commands/seed/plan.js +64 -13
- package/dist/commands/seed/plan.js.map +1 -1
- package/dist/commands/seed/seed.js +6 -9
- package/dist/commands/seed/seed.js.map +1 -1
- package/dist/commands/show/show.js +3 -7
- package/dist/commands/show/show.js.map +1 -1
- package/dist/commands/status/print.js +13 -4
- package/dist/commands/status/print.js.map +1 -1
- package/dist/commands/status/status.js +3 -7
- package/dist/commands/status/status.js.map +1 -1
- package/dist/commands/steps/steps.js +3 -7
- package/dist/commands/steps/steps.js.map +1 -1
- package/dist/commands/subsystem/edit.js +7 -7
- package/dist/commands/subsystem/edit.js.map +1 -1
- package/dist/commands/subsystem/move.js +60 -25
- package/dist/commands/subsystem/move.js.map +1 -1
- package/dist/commands/subsystem/query.js +2 -4
- package/dist/commands/subsystem/query.js.map +1 -1
- package/dist/commands/subsystem/subsystem.js +4 -7
- package/dist/commands/subsystem/subsystem.js.map +1 -1
- package/dist/commands/subsystem/sync.js +141 -71
- package/dist/commands/subsystem/sync.js.map +1 -1
- package/dist/commands/subsystem/txn/report.js +55 -5
- package/dist/commands/subsystem/txn/report.js.map +1 -1
- package/dist/commands/subsystem/txn/txn.js +121 -45
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +82 -12
- package/dist/commands/subsystem/txn/views.js.map +1 -1
- package/dist/commands/tasks/render.js +116 -0
- package/dist/commands/tasks/render.js.map +1 -0
- package/dist/commands/tasks/tasks.js +113 -0
- package/dist/commands/tasks/tasks.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +6 -9
- package/dist/commands/unarchive/manifest.js.map +1 -1
- package/dist/commands/unarchive/restore.js +17 -28
- package/dist/commands/unarchive/restore.js.map +1 -1
- package/dist/commands/unarchive/unarchive.js +22 -30
- package/dist/commands/unarchive/unarchive.js.map +1 -1
- package/dist/commands/validate/arch-coverage.js +6 -15
- package/dist/commands/validate/arch-coverage.js.map +1 -1
- package/dist/commands/validate/checks/fleet-shape.js +4 -49
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
- package/dist/commands/validate/checks/health-deps.js +9 -6
- package/dist/commands/validate/checks/health-deps.js.map +1 -1
- package/dist/commands/validate/checks/requirements.js +18 -8
- package/dist/commands/validate/checks/requirements.js.map +1 -1
- package/dist/commands/validate/checks/sources.js +6 -2
- package/dist/commands/validate/checks/sources.js.map +1 -1
- package/dist/commands/validate/feature.js +23 -19
- package/dist/commands/validate/feature.js.map +1 -1
- package/dist/commands/validate/fleet/census.js +26 -3
- package/dist/commands/validate/fleet/census.js.map +1 -1
- package/dist/commands/validate/fleet/landscape.js +35 -32
- package/dist/commands/validate/fleet/landscape.js.map +1 -1
- package/dist/commands/validate/fleet/map/attest.js +7 -12
- package/dist/commands/validate/fleet/map/attest.js.map +1 -1
- package/dist/commands/validate/fleet/map/bindings.js +16 -5
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -1
- package/dist/commands/validate/fleet/map/consumers.js +43 -122
- package/dist/commands/validate/fleet/map/consumers.js.map +1 -1
- package/dist/commands/validate/fleet/map/usecases.js +5 -11
- package/dist/commands/validate/fleet/map/usecases.js.map +1 -1
- package/dist/commands/validate/fleet/obligations.js +1 -5
- package/dist/commands/validate/fleet/obligations.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/print.js +5 -1
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
- package/dist/commands/validate/fleet/usecases/usecases.js +6 -4
- package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -1
- package/dist/commands/validate/fleet/views/ids.js +33 -7
- package/dist/commands/validate/fleet/views/ids.js.map +1 -1
- package/dist/commands/validate/fleet/views/root-documents.js +17 -35
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -1
- package/dist/commands/validate/fleet/views/stale.js +31 -14
- package/dist/commands/validate/fleet/views/stale.js.map +1 -1
- package/dist/commands/validate/links/corpus.js +1 -4
- package/dist/commands/validate/links/corpus.js.map +1 -1
- package/dist/commands/validate/links/glossary.js.map +1 -1
- package/dist/commands/validate/report.js +5 -4
- package/dist/commands/validate/report.js.map +1 -1
- package/dist/commands/validate/scope.js +113 -0
- package/dist/commands/validate/scope.js.map +1 -0
- package/dist/commands/validate/service/api.js +16 -104
- package/dist/commands/validate/service/api.js.map +1 -1
- package/dist/commands/validate/service/contracts/generated.js +134 -0
- package/dist/commands/validate/service/contracts/generated.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +1 -1
- package/dist/commands/validate/service/events/events.js.map +1 -1
- package/dist/commands/validate/service/events/message-producers.js +1 -1
- package/dist/commands/validate/service/events/message-producers.js.map +1 -1
- package/dist/commands/validate/service/evidence-pins.js +56 -10
- package/dist/commands/validate/service/evidence-pins.js.map +1 -1
- package/dist/commands/validate/service/service.js +16 -26
- package/dist/commands/validate/service/service.js.map +1 -1
- package/dist/commands/validate/service/specs.js +31 -38
- package/dist/commands/validate/service/specs.js.map +1 -1
- package/dist/commands/validate/service/spine.js +8 -11
- package/dist/commands/validate/service/spine.js.map +1 -1
- package/dist/commands/validate/service/usecases/usecases.js +3 -3
- package/dist/commands/validate/service/usecases/usecases.js.map +1 -1
- package/dist/commands/validate/validate.js +44 -26
- package/dist/commands/validate/validate.js.map +1 -1
- package/dist/commands/verify/cross/diff.js +2 -6
- package/dist/commands/verify/cross/diff.js.map +1 -1
- package/dist/commands/verify/evidence/contract.js +1 -1
- package/dist/commands/verify/evidence/contract.js.map +1 -1
- package/dist/commands/verify/evidence/read.js +12 -3
- package/dist/commands/verify/evidence/read.js.map +1 -1
- package/dist/commands/verify/frozen.js +45 -23
- package/dist/commands/verify/frozen.js.map +1 -1
- package/dist/commands/verify/record.js +69 -17
- package/dist/commands/verify/record.js.map +1 -1
- package/dist/commands/verify/report.js +27 -44
- package/dist/commands/verify/report.js.map +1 -1
- package/dist/commands/verify/results.js +112 -30
- package/dist/commands/verify/results.js.map +1 -1
- package/dist/commands/verify/verify.js +54 -51
- package/dist/commands/verify/verify.js.map +1 -1
- package/dist/commands/vouch/pack/pack.js +2 -2
- package/dist/commands/vouch/pack/pack.js.map +1 -1
- package/dist/commands/vouch/pack/print.js +1 -6
- package/dist/commands/vouch/pack/print.js.map +1 -1
- package/dist/commands/vouch/pack/sections.js +1 -2
- package/dist/commands/vouch/pack/sections.js.map +1 -1
- package/dist/commands/vouch/run.js +26 -81
- package/dist/commands/vouch/run.js.map +1 -1
- package/dist/commands/vouch/sample/plan.js +20 -34
- package/dist/commands/vouch/sample/plan.js.map +1 -1
- package/dist/commands/vouch/vet/verify.js +39 -7
- package/dist/commands/vouch/vet/verify.js.map +1 -1
- package/dist/commands/vouch/vouch.js +10 -17
- package/dist/commands/vouch/vouch.js.map +1 -1
- package/dist/core/agent/agents-md/command-map.js +57 -102
- package/dist/core/agent/agents-md/command-map.js.map +1 -1
- package/dist/core/agent/agents-md/map/explain.js +4 -3
- package/dist/core/agent/agents-md/map/explain.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
- package/dist/core/agent/agents-md/map/mcp.js +10 -7
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
- package/dist/core/agent/agents-md/map/status.js +9 -3
- package/dist/core/agent/agents-md/map/status.js.map +1 -1
- package/dist/core/agent/agents-md/map/subsystem.js +31 -17
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
- package/dist/core/agent/agents-md/map/usecases.js +24 -6
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
- package/dist/core/agent/agents-md/refusals.js +37 -21
- package/dist/core/agent/agents-md/refusals.js.map +1 -1
- package/dist/core/agent/agents-md.js +25 -10
- package/dist/core/agent/agents-md.js.map +1 -1
- package/dist/core/agent/agents-stamp.js +10 -0
- package/dist/core/agent/agents-stamp.js.map +1 -1
- package/dist/core/agent/scaffold.js +10 -15
- package/dist/core/agent/scaffold.js.map +1 -1
- package/dist/core/agent/tools/dialects.js +1 -0
- package/dist/core/agent/tools/dialects.js.map +1 -1
- package/dist/core/agent/tools/registry.js +41 -76
- package/dist/core/agent/tools/registry.js.map +1 -1
- package/dist/core/agent/workflows/adopt.js +5 -3
- package/dist/core/agent/workflows/adopt.js.map +1 -1
- package/dist/core/agent/workflows/check.js +25 -22
- package/dist/core/agent/workflows/check.js.map +1 -1
- package/dist/core/agent/workflows/closing.js +54 -21
- package/dist/core/agent/workflows/closing.js.map +1 -1
- package/dist/core/agent/workflows/feature.js +79 -11
- package/dist/core/agent/workflows/feature.js.map +1 -1
- package/dist/core/agent/workflows/implement.js +91 -13
- package/dist/core/agent/workflows/implement.js.map +1 -1
- package/dist/core/agent/workflows/reference/authoring.js +3 -1
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
- package/dist/core/agent/workflows/reference/codes.js +8 -6
- package/dist/core/agent/workflows/reference/codes.js.map +1 -1
- package/dist/core/agent/workflows/reference/done-check.js +37 -15
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
- package/dist/core/agent/workflows/reference/reference.js.map +1 -1
- package/dist/core/agent/workflows/reference/spine.js +62 -24
- package/dist/core/agent/workflows/reference/spine.js.map +1 -1
- package/dist/core/asyncapi/baseline/plan.js +14 -81
- package/dist/core/asyncapi/baseline/plan.js.map +1 -1
- package/dist/core/asyncapi/digest.js +30 -25
- package/dist/core/asyncapi/digest.js.map +1 -1
- package/dist/core/asyncapi/merge/components.js +5 -4
- package/dist/core/asyncapi/merge/components.js.map +1 -1
- package/dist/core/asyncapi/merge/error.js +7 -2
- package/dist/core/asyncapi/merge/error.js.map +1 -1
- package/dist/core/asyncapi/merge/markers.js +30 -50
- package/dist/core/asyncapi/merge/markers.js.map +1 -1
- package/dist/core/asyncapi/merge/merge.js +31 -53
- package/dist/core/asyncapi/merge/merge.js.map +1 -1
- package/dist/core/asyncapi/merge/pin.js +10 -36
- package/dist/core/asyncapi/merge/pin.js.map +1 -1
- package/dist/core/asyncapi/model.js.map +1 -1
- package/dist/core/asyncapi/read.js +36 -12
- package/dist/core/asyncapi/read.js.map +1 -1
- package/dist/core/brief/brief.js +30 -25
- package/dist/core/brief/brief.js.map +1 -1
- package/dist/core/brief/landscape.js +8 -12
- package/dist/core/brief/landscape.js.map +1 -1
- package/dist/core/brief/map/extend.js +8 -5
- package/dist/core/brief/map/extend.js.map +1 -1
- package/dist/core/brief/map/owed.js +8 -26
- package/dist/core/brief/map/owed.js.map +1 -1
- package/dist/core/brief/targets.js +79 -5
- package/dist/core/brief/targets.js.map +1 -1
- package/dist/core/brief/unchecked.js +1 -1
- package/dist/core/brief/unchecked.js.map +1 -1
- package/dist/core/c4/likec4.js +147 -16
- package/dist/core/c4/likec4.js.map +1 -1
- package/dist/core/c4/locate/unlocated.js +150 -0
- package/dist/core/c4/locate/unlocated.js.map +1 -0
- package/dist/core/c4/parsed/values.js +54 -1
- package/dist/core/c4/parsed/values.js.map +1 -1
- package/dist/core/c4/prefetch/fleet.js +1 -9
- package/dist/core/c4/prefetch/fleet.js.map +1 -1
- package/dist/core/c4/project/architecture.js +31 -9
- package/dist/core/c4/project/architecture.js.map +1 -1
- package/dist/core/c4/project/documents.js +36 -10
- package/dist/core/c4/project/documents.js.map +1 -1
- package/dist/core/c4/project/load.js +14 -21
- package/dist/core/c4/project/load.js.map +1 -1
- package/dist/core/c4/project/staged.js +7 -3
- package/dist/core/c4/project/staged.js.map +1 -1
- package/dist/core/c4/resolve/external.js +51 -0
- package/dist/core/c4/resolve/external.js.map +1 -0
- package/dist/core/c4/resolve/service.js +25 -1
- package/dist/core/c4/resolve/service.js.map +1 -1
- package/dist/core/c4/seed/template.js +15 -1
- package/dist/core/c4/seed/template.js.map +1 -1
- package/dist/core/c4/service-model/shape.js +2 -22
- package/dist/core/c4/service-model/shape.js.map +1 -1
- package/dist/core/c4/source-mask.js +288 -4
- package/dist/core/c4/source-mask.js.map +1 -1
- package/dist/core/c4/source-scan.js +203 -147
- package/dist/core/c4/source-scan.js.map +1 -1
- package/dist/core/c4/splice/authored-source.js +19 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -1
- package/dist/core/c4/splice/delta-blocks.js +5 -18
- package/dist/core/c4/splice/delta-blocks.js.map +1 -1
- package/dist/core/c4/splice/landscape-merge.js +13 -8
- package/dist/core/c4/splice/landscape-merge.js.map +1 -1
- package/dist/core/c4/splice/model/merge.js +10 -6
- package/dist/core/c4/splice/model/merge.js.map +1 -1
- package/dist/core/c4/splice/placement.js +108 -18
- package/dist/core/c4/splice/placement.js.map +1 -1
- package/dist/core/c4/workspace.js +13 -22
- package/dist/core/c4/workspace.js.map +1 -1
- package/dist/core/capabilities/capabilities.js +6 -6
- package/dist/core/capabilities/capabilities.js.map +1 -1
- package/dist/core/capabilities/delta/doc.js +7 -10
- package/dist/core/capabilities/delta/doc.js.map +1 -1
- package/dist/core/capabilities/findings.js +15 -20
- package/dist/core/capabilities/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/findings.js +12 -19
- package/dist/core/capabilities/realizes/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/join.js +0 -20
- package/dist/core/capabilities/realizes/join.js.map +1 -1
- package/dist/core/capabilities/rollup.js +6 -0
- package/dist/core/capabilities/rollup.js.map +1 -1
- package/dist/core/capabilities/tree.js +6 -28
- package/dist/core/capabilities/tree.js.map +1 -1
- package/dist/core/capabilities/usecase-join.js +3 -13
- package/dist/core/capabilities/usecase-join.js.map +1 -1
- package/dist/core/coherence/authoring/scaffold.js +11 -3
- package/dist/core/coherence/authoring/scaffold.js.map +1 -1
- package/dist/core/coherence/authoring/sentinels.js +2 -1
- package/dist/core/coherence/authoring/sentinels.js.map +1 -1
- package/dist/core/coherence/coherence.js +30 -16
- package/dist/core/coherence/coherence.js.map +1 -1
- package/dist/core/coherence/consumed/operations.js +70 -0
- package/dist/core/coherence/consumed/operations.js.map +1 -0
- package/dist/core/coherence/consumed/refusal.js +38 -0
- package/dist/core/coherence/consumed/refusal.js.map +1 -0
- package/dist/core/coherence/declared.js +60 -34
- package/dist/core/coherence/declared.js.map +1 -1
- package/dist/core/coherence/events/declared.js +44 -59
- package/dist/core/coherence/events/declared.js.map +1 -1
- package/dist/core/coherence/events/events.js +119 -8
- package/dist/core/coherence/events/events.js.map +1 -1
- package/dist/core/coherence/events/grades.js +31 -8
- package/dist/core/coherence/events/grades.js.map +1 -1
- package/dist/core/coherence/events/lookups.js +14 -82
- package/dist/core/coherence/events/lookups.js.map +1 -1
- package/dist/core/coherence/lookups.js +18 -78
- package/dist/core/coherence/lookups.js.map +1 -1
- package/dist/core/coherence/retired/archived.js +285 -0
- package/dist/core/coherence/retired/archived.js.map +1 -0
- package/dist/core/coherence/retired/event-pair.js +62 -0
- package/dist/core/coherence/retired/event-pair.js.map +1 -0
- package/dist/core/coherence/retired/merged.js +79 -0
- package/dist/core/coherence/retired/merged.js.map +1 -0
- package/dist/core/coherence/retired/retirement.js +49 -0
- package/dist/core/coherence/retired/retirement.js.map +1 -0
- package/dist/core/coherence/retired/revival/arrivals.js +131 -0
- package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
- package/dist/core/coherence/retired/revival/revived.js +297 -0
- package/dist/core/coherence/retired/revival/revived.js.map +1 -0
- package/dist/core/consumers/after.js +113 -0
- package/dist/core/consumers/after.js.map +1 -0
- package/dist/core/consumers/consumers.js +296 -0
- package/dist/core/consumers/consumers.js.map +1 -0
- package/dist/core/consumers/current.js +211 -0
- package/dist/core/consumers/current.js.map +1 -0
- package/dist/core/consumers/scan.js +46 -0
- package/dist/core/consumers/scan.js.map +1 -0
- package/dist/core/consumers/state.js +27 -0
- package/dist/core/consumers/state.js.map +1 -0
- package/dist/core/delta/delta.js +1 -1
- package/dist/core/delta/delta.js.map +1 -1
- package/dist/core/deployment/delta.js +15 -16
- package/dist/core/deployment/delta.js.map +1 -1
- package/dist/core/diff/base-git.js +94 -12
- package/dist/core/diff/base-git.js.map +1 -1
- package/dist/core/diff/base-state.js +28 -16
- package/dist/core/diff/base-state.js.map +1 -1
- package/dist/core/diff/joins.js +136 -0
- package/dist/core/diff/joins.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +22 -6
- package/dist/core/diff/scope/changed-paths.js.map +1 -1
- package/dist/core/diff/scope/edges.js +82 -0
- package/dist/core/diff/scope/edges.js.map +1 -0
- package/dist/core/diff/semantic.js +100 -31
- package/dist/core/diff/semantic.js.map +1 -1
- package/dist/core/doctor/agents.js +34 -18
- package/dist/core/doctor/agents.js.map +1 -1
- package/dist/core/doctor/report.js.map +1 -1
- package/dist/core/document/apply.js +10 -2
- package/dist/core/document/apply.js.map +1 -1
- package/dist/core/document/frontmatter.js +55 -0
- package/dist/core/document/frontmatter.js.map +1 -1
- package/dist/core/document/parse.js +29 -6
- package/dist/core/document/parse.js.map +1 -1
- package/dist/core/document/scenarios.js +17 -0
- package/dist/core/document/scenarios.js.map +1 -1
- package/dist/core/document/spec.js +4 -11
- package/dist/core/document/spec.js.map +1 -1
- package/dist/core/envelope/config.js +10 -0
- package/dist/core/envelope/config.js.map +1 -1
- package/dist/core/envelope/json.js +34 -0
- package/dist/core/envelope/json.js.map +1 -1
- package/dist/core/explain/families.js +17 -12
- package/dist/core/explain/families.js.map +1 -1
- package/dist/core/explain/refusals.js +3 -3
- package/dist/core/explain/refusals.js.map +1 -1
- package/dist/core/explain/terms.js +18 -9
- package/dist/core/explain/terms.js.map +1 -1
- package/dist/core/explore/explore.js +2 -7
- package/dist/core/explore/explore.js.map +1 -1
- package/dist/core/fleet-context.js +34 -95
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +10 -3
- package/dist/core/gate/checks.js.map +1 -1
- package/dist/core/gate/partners.js +64 -19
- package/dist/core/gate/partners.js.map +1 -1
- package/dist/core/gate/report.js.map +1 -1
- package/dist/core/gate/verification.js +75 -12
- package/dist/core/gate/verification.js.map +1 -1
- package/dist/core/gherkin/stale.js +13 -37
- package/dist/core/gherkin/stale.js.map +1 -1
- package/dist/core/glossary/tree.js +6 -19
- package/dist/core/glossary/tree.js.map +1 -1
- package/dist/core/kernel/concurrency.js +19 -0
- package/dist/core/kernel/concurrency.js.map +1 -1
- package/dist/core/kernel/records.js +11 -0
- package/dist/core/kernel/records.js.map +1 -1
- package/dist/core/links/backlinks.js +1 -3
- package/dist/core/links/backlinks.js.map +1 -1
- package/dist/core/links/corpus.js +5 -36
- package/dist/core/links/corpus.js.map +1 -1
- package/dist/core/mcp/argv.js +101 -0
- package/dist/core/mcp/argv.js.map +1 -1
- package/dist/core/mcp/author-tools.js +2 -1
- package/dist/core/mcp/author-tools.js.map +1 -1
- package/dist/core/mcp/protocol.js +2 -2
- package/dist/core/mcp/protocol.js.map +1 -1
- package/dist/core/mcp/tools.js +43 -33
- package/dist/core/mcp/tools.js.map +1 -1
- package/dist/core/openapi/baseline/gate.js +53 -12
- package/dist/core/openapi/baseline/gate.js.map +1 -1
- package/dist/core/openapi/baseline/plan.js +22 -8
- package/dist/core/openapi/baseline/plan.js.map +1 -1
- package/dist/core/openapi/baseline/record.js +0 -1
- package/dist/core/openapi/baseline/record.js.map +1 -1
- package/dist/core/openapi/contract-text.js +33 -0
- package/dist/core/openapi/contract-text.js.map +1 -0
- package/dist/core/openapi/contract-tree.js +56 -0
- package/dist/core/openapi/contract-tree.js.map +1 -0
- package/dist/core/openapi/depth.js +38 -24
- package/dist/core/openapi/depth.js.map +1 -1
- package/dist/core/openapi/digest.js +52 -2
- package/dist/core/openapi/digest.js.map +1 -1
- package/dist/core/openapi/doc.js +12 -4
- package/dist/core/openapi/doc.js.map +1 -1
- package/dist/core/openapi/merge/components.js +25 -60
- package/dist/core/openapi/merge/components.js.map +1 -1
- package/dist/core/openapi/merge/error.js +3 -13
- package/dist/core/openapi/merge/error.js.map +1 -1
- package/dist/core/openapi/merge/markers.js +26 -30
- package/dist/core/openapi/merge/markers.js.map +1 -1
- package/dist/core/openapi/merge/merge.js +26 -52
- package/dist/core/openapi/merge/merge.js.map +1 -1
- package/dist/core/openapi/merge/pin.js +15 -35
- package/dist/core/openapi/merge/pin.js.map +1 -1
- package/dist/core/openspec/decide/verdict.js.map +1 -1
- package/dist/core/openspec/inventory.js +4 -5
- package/dist/core/openspec/inventory.js.map +1 -1
- package/dist/core/openspec/model/mapping.js +30 -28
- package/dist/core/openspec/model/mapping.js.map +1 -1
- package/dist/core/openspec/model/model.js +12 -7
- package/dist/core/openspec/model/model.js.map +1 -1
- package/dist/core/openspec/scan/changes.js +10 -4
- package/dist/core/openspec/scan/changes.js.map +1 -1
- package/dist/core/openspec/scan/config.js +7 -16
- package/dist/core/openspec/scan/config.js.map +1 -1
- package/dist/core/openspec/scan/shape.js +63 -19
- package/dist/core/openspec/scan/shape.js.map +1 -1
- package/dist/core/openspec/scan/walk.js +21 -38
- package/dist/core/openspec/scan/walk.js.map +1 -1
- package/dist/core/pack/living.js +2 -16
- package/dist/core/pack/living.js.map +1 -1
- package/dist/core/provenance/findings.js +48 -12
- package/dist/core/provenance/findings.js.map +1 -1
- package/dist/core/provenance/git.js +33 -57
- package/dist/core/provenance/git.js.map +1 -1
- package/dist/core/provenance/gitq/moves.js +71 -114
- package/dist/core/provenance/gitq/moves.js.map +1 -1
- package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
- package/dist/core/provenance/sources.js +23 -6
- package/dist/core/provenance/sources.js.map +1 -1
- package/dist/core/provenance/stamp.js +5 -17
- package/dist/core/provenance/stamp.js.map +1 -1
- package/dist/core/repo/entries.js.map +1 -1
- package/dist/core/repo/paths.js +34 -29
- package/dist/core/repo/paths.js.map +1 -1
- package/dist/core/repo/repo.js +24 -14
- package/dist/core/repo/repo.js.map +1 -1
- package/dist/core/repo/tree/find.js +12 -3
- package/dist/core/repo/tree/find.js.map +1 -1
- package/dist/core/repo/tree/fs.js +27 -1
- package/dist/core/repo/tree/fs.js.map +1 -1
- package/dist/core/repo/tree/render/body.js +45 -6
- package/dist/core/repo/tree/render/body.js.map +1 -1
- package/dist/core/repo/tree/render/service/header.js +125 -0
- package/dist/core/repo/tree/render/service/header.js.map +1 -0
- package/dist/core/repo/tree/render/service/peers.js +28 -0
- package/dist/core/repo/tree/render/service/peers.js.map +1 -0
- package/dist/core/repo/tree/render/service/view-id.js +67 -0
- package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
- package/dist/core/repo/tree/render/service/views.js +78 -0
- package/dist/core/repo/tree/render/service/views.js.map +1 -0
- package/dist/core/repo/tree/render/stale.js +19 -2
- package/dist/core/repo/tree/render/stale.js.map +1 -1
- package/dist/core/repo/tree/render/views.js +102 -33
- package/dist/core/repo/tree/render/views.js.map +1 -1
- package/dist/core/repo/tree/walk.js +8 -30
- package/dist/core/repo/tree/walk.js.map +1 -1
- package/dist/core/results.js +47 -12
- package/dist/core/results.js.map +1 -1
- package/dist/core/staging/commit.js +10 -12
- package/dist/core/staging/commit.js.map +1 -1
- package/dist/core/staging/lock.js +74 -12
- package/dist/core/staging/lock.js.map +1 -1
- package/dist/core/staging/recovery/intent.js +2 -5
- package/dist/core/staging/recovery/intent.js.map +1 -1
- package/dist/core/staging/recovery/recover.js +60 -1
- package/dist/core/staging/recovery/recover.js.map +1 -1
- package/dist/core/staging/snapshot/manifest.js +152 -0
- package/dist/core/staging/snapshot/manifest.js.map +1 -0
- package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +10 -34
- package/dist/core/staging/snapshot/snapshot.js.map +1 -0
- package/dist/core/status/actions/execution.js +11 -0
- package/dist/core/status/actions/execution.js.map +1 -1
- package/dist/core/status/feature/artifacts.js +2 -2
- package/dist/core/status/feature/artifacts.js.map +1 -1
- package/dist/core/status/feature/feature.js +5 -11
- package/dist/core/status/feature/feature.js.map +1 -1
- package/dist/core/status/fleet/fleet.js +6 -2
- package/dist/core/status/fleet/fleet.js.map +1 -1
- package/dist/core/status/fleet/next.js +26 -3
- package/dist/core/status/fleet/next.js.map +1 -1
- package/dist/core/status/report.js.map +1 -1
- package/dist/core/status/verification.js +30 -15
- package/dist/core/status/verification.js.map +1 -1
- package/dist/core/tasks/packet.js +174 -0
- package/dist/core/tasks/packet.js.map +1 -0
- package/dist/core/tasks/record.js +43 -0
- package/dist/core/tasks/record.js.map +1 -0
- package/dist/core/tasks/sources.js +181 -0
- package/dist/core/tasks/sources.js.map +1 -0
- package/dist/core/tasks/stamps.js +76 -0
- package/dist/core/tasks/stamps.js.map +1 -0
- package/dist/core/tasks/unit.js +0 -0
- package/dist/core/tasks/unit.js.map +1 -0
- package/dist/core/usecases/capability.js +11 -14
- package/dist/core/usecases/capability.js.map +1 -1
- package/dist/core/usecases/delta/flows.js +5 -7
- package/dist/core/usecases/delta/flows.js.map +1 -1
- package/dist/core/usecases/fleet.js +178 -31
- package/dist/core/usecases/fleet.js.map +1 -1
- package/dist/core/usecases/operations.js +5 -4
- package/dist/core/usecases/operations.js.map +1 -1
- package/dist/core/usecases/service/flows.js +9 -6
- package/dist/core/usecases/service/flows.js.map +1 -1
- package/dist/core/usecases/suites/participants.js +83 -0
- package/dist/core/usecases/suites/participants.js.map +1 -0
- package/dist/core/usecases/suites/suites.js +45 -0
- package/dist/core/usecases/suites/suites.js.map +1 -0
- package/dist/core/verify/answers.js +12 -5
- package/dist/core/verify/answers.js.map +1 -1
- package/dist/core/verify/build.js +102 -82
- package/dist/core/verify/build.js.map +1 -1
- package/dist/core/verify/checklist.js +45 -10
- package/dist/core/verify/checklist.js.map +1 -1
- package/dist/core/verify/location.js +67 -0
- package/dist/core/verify/location.js.map +1 -0
- package/dist/core/verify/pins/lint.js +3 -2
- package/dist/core/verify/pins/lint.js.map +1 -1
- package/dist/core/verify/pins/pin.js +1 -1
- package/dist/core/verify/pins/pin.js.map +1 -1
- package/dist/core/verify/read/file.js +224 -0
- package/dist/core/verify/read/file.js.map +1 -0
- package/dist/core/verify/{file.js → read/shape.js} +16 -104
- package/dist/core/verify/read/shape.js.map +1 -0
- package/dist/core/verify/read/union.js +105 -0
- package/dist/core/verify/read/union.js.map +1 -0
- package/dist/core/verify/record.js +35 -12
- package/dist/core/verify/record.js.map +1 -1
- package/dist/core/verify/store/commit.js +40 -30
- package/dist/core/verify/store/commit.js.map +1 -1
- package/dist/core/verify/store/render.js +8 -4
- package/dist/core/verify/store/render.js.map +1 -1
- package/dist/core/vocabulary/issue.js +1 -0
- package/dist/core/vocabulary/issue.js.map +1 -1
- package/dist/core/vocabulary/maturity.js +53 -3
- package/dist/core/vocabulary/maturity.js.map +1 -1
- package/examples/README.md +28 -9
- package/examples/docs/architecture/landscape.likec4 +21 -7
- package/examples/docs/architecture/services.likec4 +70 -0
- package/examples/docs/architecture/subsystems.likec4 +3 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +6 -2
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
- package/examples/docs/services/order-service/asyncapi.yaml +3 -2
- package/examples/docs/services/order-service/model.likec4 +10 -8
- package/examples/docs/services/payment-service/model.likec4 +6 -4
- package/examples/docs/services/platform/identity-service/model.likec4 +9 -7
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
- package/examples/docs/services/platform/notification-service/model.likec4 +4 -5
- package/package.json +1 -1
- package/dist/commands/validate/checks/vocabulary.js +0 -16
- package/dist/commands/validate/checks/vocabulary.js.map +0 -1
- package/dist/core/agent/agents-md/artifacts.js +0 -221
- package/dist/core/agent/agents-md/artifacts.js.map +0 -1
- package/dist/core/agent/agents-md/cycle.js +0 -180
- package/dist/core/agent/agents-md/cycle.js.map +0 -1
- package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
- package/dist/core/agent/agents-md/spine.js +0 -40
- package/dist/core/agent/agents-md/spine.js.map +0 -1
- package/dist/core/coherence/events/merged.js +0 -40
- package/dist/core/coherence/events/merged.js.map +0 -1
- package/dist/core/diff/current-state.js +0 -96
- package/dist/core/diff/current-state.js.map +0 -1
- package/dist/core/diff/victims.js +0 -256
- package/dist/core/diff/victims.js.map +0 -1
- package/dist/core/staging/snapshot.js.map +0 -1
- package/dist/core/verify/file.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../../../src/core/status/feature/artifacts.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The artifact table: which files a feature owes, whether each is on disk, and\n * which finding turns a row `draft`. Nothing is graded here — the shared\n * checks already ran, and this module only attributes what they reported to\n * the file a reader should edit.\n */\nimport { existsSync } from \"node:fs\";\nimport { repoPath } from \"../../envelope/json.js\";\nimport type { CapabilityDoc } from \"../../capabilities/tree.js\";\nimport type { PathableService } from \"../../kernel/ids/service.js\";\nimport type { FeatureEntry } from \"../../repo/entries.js\";\nimport { featurePaths, featureSpecPaths } from \"../../repo/paths.js\";\nimport { verificationPath } from \"../../verify/record.js\";\nimport type { Finding } from \"../../vocabulary/report.js\";\nimport { owesContract } from \"../contracts.js\";\nimport type { ServiceEntry } from \"../../repo/entries.js\";\nimport type { ArtifactId, ArtifactState, ArtifactStatus, VerificationState } from \"../report.js\";\nimport { verificationStatus } from \"../verification.js\";\nimport type { DocsDir } from \"../../kernel/ids/dirs.js\";\n\n/**\n * The `capability.*` codes filed against a capability DOCUMENT rather than\n * against a service requirement's join line. Spelled out rather than matched by\n * prefix because the family straddles both corpora, and the whole point of this\n * table is to name one file.\n */\nconst CAPABILITY_DOCUMENT_CODES = new Set([\n \"capability.requirement-unidentified\",\n \"capability.requirement-service-scoped\",\n \"capability.requirement-inert-join\",\n \"capability.uncovered\",\n \"capability.remove-requirement-realized\",\n]);\n\n/**\n * Which artifact an error names.\n *\n * Attribution is by code family, because that is the only machine-readable\n * pointer an `Issue` carries: `subject` narrows to a NAME, never to a file.\n * The families are unambiguous — `c4-api.*`/`c4-event.*`/`c4.*` and the two\n * `delta.*` codes about the LikeC4 document itself grade the architecture\n * axis, `openapi.*` grades the API contract, `asyncapi.*` the event contract,\n * and the remaining `delta.*` codes plus `spec-api.*`/`spec-event.*` are the\n * delta-shape checks, every one of which is about a requirement in a spec\n * delta.\n *\n * An error that maps to nothing (or whose subject is unknown) still lands in\n * `checks.issues` and still drives `next.fix-coherence` — it just does not turn\n * any single artifact `draft`, because guessing which file to blame is how a\n * reader gets sent to edit the wrong one.\n *\n * THE BUSINESS CORPUS IS THE ONE PLACE THE CODE FAMILY IS NOT ENOUGH. A\n * `delta.*` issue's `subject` is a service id OR a capability id\n * (`core/delta/scope.ts`), and so is a `scaffold.placeholder`'s\n * (`core/coherence/authoring/scaffold.ts` files one per unedited capability\n * delta), and nothing in either code says which — so this\n * function answers `spec` for both and `featureArtifacts` below resolves it by\n * MEMBERSHIP, against the two lists it already holds. Where a name is only a\n * capability the capability row is turned `draft` and no service row is; where\n * a fleet holds a service AND a capability of the same name, BOTH are, which is\n * the pessimistic answer this module is required to give and strictly better\n * than the old one (it named the service's spec delta and was silently wrong).\n * The capability-document codes below need no such resolution: they are only\n * ever filed against a capability.\n */\nfunction faultedArtifact(code: string, subject?: string): ArtifactId | null {\n if (code === \"delta.invalid\" || code === \"delta.nothing-tagged\") return \"delta\";\n if (code.startsWith(\"c4-api.\") || code.startsWith(\"c4-event.\") || code.startsWith(\"c4.\")) return \"delta\";\n if (code.startsWith(\"openapi.\")) return \"openapi\";\n if (code.startsWith(\"asyncapi.\")) return \"asyncapi\";\n // The capability document's own grades, and the two halves of the `Realizes:`\n // join a feature can break — every one of them names a\n // `features/<FEAT>/capabilities/<id>/spec.md`, never a service delta. The two\n // `capability.*` codes deliberately absent are `capability.unknown` and\n // `capability.realizes-unknown`: those grade a SERVICE requirement's join\n // lines and carry a service id, so the prefix alone would send their reader\n // to the wrong file.\n if (CAPABILITY_DOCUMENT_CODES.has(code)) return \"capabilities\";\n if (code.startsWith(\"delta.\") || code.startsWith(\"spec-api.\") || code.startsWith(\"spec-event.\")) return \"spec\";\n // The two families validate contributes: the frontmatter checks read only\n // intent.md, and a requirement with no scenario is a requirement in a spec\n // delta. Both name exactly one file, so both may turn a row `draft`.\n if (code.startsWith(\"frontmatter.\")) return \"intent\";\n if (code === \"requirements.missing-scenarios\") return \"spec\";\n // The authoring gate names exactly one file per finding too — leaving these\n // unmapped had the table calling intent.md `done` (\"nothing is owed here\")\n // while the archive exited 1 because it says nothing. A placeholder finding\n // carries no subject when it is the C4 delta's scaffolded description; with a\n // subject it is a SPEC DELTA IN EITHER CORPUS — a service id when the text\n // sits in `specs/<svc>/`, a capability id when it sits in\n // `capabilities/<id>/` — which is why `spec` here is resolved by membership\n // below and must not be narrowed to the service list.\n if (code === \"intent.empty\") return \"intent\";\n if (code === \"scaffold.placeholder\") return subject === undefined ? \"delta\" : \"spec\";\n return null;\n}\n\n/**\n * The artifact table. Every entry is graded by two questions in order — is it\n * there, and does anything the shared checks reported name it — so a file that\n * exists and is wrong can never be reported the way one that was never written\n * is.\n */\n/** What the grading of one feature's files needs beyond the files themselves. */\nexport interface Grading {\n /** The findings that make an artifact `draft` rather than `done`. */\n blocking: Finding[];\n verification: VerificationState;\n /** Operations some OTHER feature's contract already holds. */\n contracted: ReadonlySet<string>;\n /** Operations this feature's own requirements govern. */\n governs: ReadonlySet<string>;\n /** The enumeration's entries by id — where each living service is and what it has (owesContract reads both). */\n living: ReadonlyMap<string, ServiceEntry>;\n /**\n * The capability documents this feature's delta carries. Empty for every\n * feature in a fleet that has not adopted the business axis, which is what\n * keeps the rows out of those payloads entirely.\n */\n capabilities: readonly CapabilityDoc[];\n}\n\nexport function featureArtifacts(\n docsDir: DocsDir,\n feature: FeatureEntry,\n services: readonly PathableService[],\n grading: Grading,\n): ArtifactState[] {\n const { blocking, verification, contracted, governs, living, capabilities } = grading;\n const paths = featurePaths(feature.dir);\n const rel = (abs: string): string => repoPath(docsDir, abs);\n const faults = blocking.map((f) => ({ artifact: faultedArtifact(f.code, f.subject), subject: f.subject }));\n const faulted = (id: ArtifactId, service: string | null): boolean =>\n faults.some((f) => f.artifact === id && (service === null || f.subject === service));\n\n const out: ArtifactState[] = [\n fileState(\"intent\", { service: null, path: rel(paths.intent), exists: existsSync(paths.intent) }, true, faulted(\"intent\", null)),\n fileState(\"delta\", { service: null, path: rel(paths.delta), exists: existsSync(paths.delta) }, false, faulted(\"delta\", null)),\n ];\n\n // One row per capability document the feature carries, and none at all when\n // it carries none: the row is the answer to \"which file do I edit\", and a\n // fleet that has not adopted the axis has no such file to name. Never\n // `required` — no feature owes a business change — so absence never shows up\n // as work owed, and only a fault turns one `draft`. A `delta.*` fault carries\n // the capability id in `subject` with nothing saying it is not a service, so\n // the membership test is the resolution the code family cannot give.\n for (const doc of capabilities) {\n out.push({\n id: \"capabilities\",\n service: null,\n capability: doc.id,\n path: rel(doc.spec),\n exists: existsSync(doc.spec),\n required: false,\n status: faulted(\"capabilities\", doc.id) || faulted(\"spec\", doc.id) ? \"draft\" : \"done\",\n blockedBy: [],\n });\n }\n\n for (const svc of services) {\n const p = featureSpecPaths(feature.dir, svc);\n const specFault = faulted(\"spec\", svc);\n // What a service delta owes is a REQUIREMENT DELTA, not specifically the\n // business one. `arch.spec.md` is the other corpus, with the same grammar\n // and the same delta algebra, and `featureChecklist` derives claims from\n // it — so a directory holding only the architectural document is a\n // complete delta, and every gate already reads it as one: `coherence`'s\n // `service.no-requirement-delta` counts the DIRECTORY, and\n // `validate --feature` grades an arch-only delta clean at exit 0.\n //\n // Requiring `spec.md` unconditionally made this module the one document\n // that disagreed. An architecture-only feature — an outbox added, a retry\n // policy, an alert; the changes `arch.spec.md` exists for because no\n // business scenario was ever going to carry them — reported `missing` and\n // emitted `next.author-spec`, sending the author to write business\n // requirements the change does not have, about a feature the gate accepts.\n // status may be redder than the gates on a question they also ask; it may\n // not invent an obligation they do not.\n const archExists = existsSync(p.archSpec);\n out.push(fileState(\"spec\", { service: svc, path: rel(p.spec), exists: existsSync(p.spec) }, !archExists, specFault));\n out.push(fileState(\"arch-spec\", { service: svc, path: rel(p.archSpec), exists: archExists }, false, specFault));\n out.push(\n fileState(\n \"openapi\",\n { service: svc, path: rel(p.openapi), exists: existsSync(p.openapi) },\n owesContract(living.get(svc), contracted.has(svc), governs.has(svc)),\n faulted(\"openapi\", svc),\n ),\n );\n // Never required: an event contract is genuinely optional — the axis's own\n // absence-grading rests on that — so absence is `done`, exactly like\n // arch.spec.md. Present and faulted still turns the row `draft`.\n out.push(\n fileState(\n \"asyncapi\",\n { service: svc, path: rel(p.asyncapi), exists: existsSync(p.asyncapi) },\n false,\n faulted(\"asyncapi\", svc),\n ),\n );\n }\n\n // The record is the one artifact with a real prerequisite: its checklist is\n // derived from the delta and the per-service deltas, so with none of them\n // written there is nothing to answer — authoring one is impossible rather\n // than merely premature, which is the whole distinction `blocked` carries.\n //\n // `capabilities` is excluded for exactly that reason and not by oversight:\n // `featureChecklist` derives no claim from a capability document — a business\n // promise is realized by service requirements, and those are what the record\n // answers — so counting one as a feeder would report a capability-only\n // feature as ready to verify against a checklist with nothing in it.\n //\n // `arch-spec` used to be excluded beside it, and that failed the same test\n // the comment above states: `featureChecklist` DOES read `arch.spec.md`\n // (`core/verify/checklist.ts`), and `loam verify` on an architecture-only\n // feature returns a real checklist. The row said `blocked … needs delta,\n // spec, openapi, asyncapi` and `next.verify` said there was nothing to derive\n // a checklist from, about a command that derives one — which is the failure\n // `blocked` is supposed to prevent, pointed the wrong way.\n const feeders = out.filter((a) => a.id !== \"intent\" && a.id !== \"capabilities\" && a.exists);\n out.push({\n id: \"verification\",\n service: null,\n path: rel(verificationPath(feature.dir)),\n exists: verification.state !== \"absent\",\n required: true,\n status: feeders.length === 0 ? \"blocked\" : verificationStatus(verification),\n blockedBy: feeders.length === 0 ? [\"delta\", \"spec\", \"arch-spec\", \"openapi\", \"asyncapi\"] : [],\n });\n return out;\n}\n\n/** Where an artifact is and whether it is there — the three facts a grade reads. */\ninterface FileFacts {\n service: string | null;\n path: string;\n exists: boolean;\n}\n\nfunction fileState(id: ArtifactId, file: FileFacts, required: boolean, faulted: boolean): ArtifactState {\n const { service, path, exists } = file;\n const status: ArtifactStatus = !exists\n ? required\n ? \"missing\"\n : \"done\"\n : faulted\n ? \"draft\"\n : \"done\";\n return { id, service, path, exists, required, status, blockedBy: [] };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../../../src/core/status/feature/artifacts.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The artifact table: which files a feature owes, whether each is on disk, and\n * which finding turns a row `draft`. Nothing is graded here — the shared\n * checks already ran, and this module only attributes what they reported to\n * the file a reader should edit.\n */\nimport { existsSync } from \"node:fs\";\nimport { repoPath } from \"../../envelope/json.js\";\nimport type { CapabilityDoc } from \"../../capabilities/tree.js\";\nimport type { PathableService } from \"../../kernel/ids/service.js\";\nimport type { FeatureEntry } from \"../../repo/entries.js\";\nimport { featurePaths, featureSpecPaths } from \"../../repo/paths.js\";\nimport { recordLocation } from \"../../verify/location.js\";\nimport type { Finding } from \"../../vocabulary/report.js\";\nimport { owesContract } from \"../contracts.js\";\nimport type { ServiceEntry } from \"../../repo/entries.js\";\nimport type { ArtifactId, ArtifactState, ArtifactStatus, VerificationState } from \"../report.js\";\nimport { verificationStatus } from \"../verification.js\";\nimport type { DocsDir } from \"../../kernel/ids/dirs.js\";\n\n/**\n * The `capability.*` codes filed against a capability DOCUMENT rather than\n * against a service requirement's join line. Spelled out rather than matched by\n * prefix because the family straddles both corpora, and the whole point of this\n * table is to name one file.\n */\nconst CAPABILITY_DOCUMENT_CODES = new Set([\n \"capability.requirement-unidentified\",\n \"capability.requirement-service-scoped\",\n \"capability.requirement-inert-join\",\n \"capability.uncovered\",\n \"capability.remove-requirement-realized\",\n]);\n\n/**\n * Which artifact an error names.\n *\n * Attribution is by code family, because that is the only machine-readable\n * pointer an `Issue` carries: `subject` narrows to a NAME, never to a file.\n * The families are unambiguous — `c4-api.*`/`c4-event.*`/`c4.*` and the two\n * `delta.*` codes about the LikeC4 document itself grade the architecture\n * axis, `openapi.*` grades the API contract, `asyncapi.*` the event contract,\n * and the remaining `delta.*` codes plus `spec-api.*`/`spec-event.*` are the\n * delta-shape checks, every one of which is about a requirement in a spec\n * delta.\n *\n * An error that maps to nothing (or whose subject is unknown) still lands in\n * `checks.issues` and still drives `next.fix-coherence` — it just does not turn\n * any single artifact `draft`, because guessing which file to blame is how a\n * reader gets sent to edit the wrong one.\n *\n * THE BUSINESS CORPUS IS THE ONE PLACE THE CODE FAMILY IS NOT ENOUGH. A\n * `delta.*` issue's `subject` is a service id OR a capability id\n * (`core/delta/scope.ts`), and so is a `scaffold.placeholder`'s\n * (`core/coherence/authoring/scaffold.ts` files one per unedited capability\n * delta), and nothing in either code says which — so this\n * function answers `spec` for both and `featureArtifacts` below resolves it by\n * MEMBERSHIP, against the two lists it already holds. Where a name is only a\n * capability the capability row is turned `draft` and no service row is; where\n * a fleet holds a service AND a capability of the same name, BOTH are, which is\n * the pessimistic answer this module is required to give and strictly better\n * than the old one (it named the service's spec delta and was silently wrong).\n * The capability-document codes below need no such resolution: they are only\n * ever filed against a capability.\n */\nfunction faultedArtifact(code: string, subject?: string): ArtifactId | null {\n if (code === \"delta.invalid\" || code === \"delta.nothing-tagged\") return \"delta\";\n if (code.startsWith(\"c4-api.\") || code.startsWith(\"c4-event.\") || code.startsWith(\"c4.\")) return \"delta\";\n if (code.startsWith(\"openapi.\")) return \"openapi\";\n if (code.startsWith(\"asyncapi.\")) return \"asyncapi\";\n // The capability document's own grades, and the two halves of the `Realizes:`\n // join a feature can break — every one of them names a\n // `features/<FEAT>/capabilities/<id>/spec.md`, never a service delta. The two\n // `capability.*` codes deliberately absent are `capability.unknown` and\n // `capability.realizes-unknown`: those grade a SERVICE requirement's join\n // lines and carry a service id, so the prefix alone would send their reader\n // to the wrong file.\n if (CAPABILITY_DOCUMENT_CODES.has(code)) return \"capabilities\";\n if (code.startsWith(\"delta.\") || code.startsWith(\"spec-api.\") || code.startsWith(\"spec-event.\")) return \"spec\";\n // The two families validate contributes: the frontmatter checks read only\n // intent.md, and a requirement with no scenario is a requirement in a spec\n // delta. Both name exactly one file, so both may turn a row `draft`.\n if (code.startsWith(\"frontmatter.\")) return \"intent\";\n if (code === \"requirements.missing-scenarios\") return \"spec\";\n // The authoring gate names exactly one file per finding too — leaving these\n // unmapped had the table calling intent.md `done` (\"nothing is owed here\")\n // while the archive exited 1 because it says nothing. A placeholder finding\n // carries no subject when it is the C4 delta's scaffolded description; with a\n // subject it is a SPEC DELTA IN EITHER CORPUS — a service id when the text\n // sits in `specs/<svc>/`, a capability id when it sits in\n // `capabilities/<id>/` — which is why `spec` here is resolved by membership\n // below and must not be narrowed to the service list.\n if (code === \"intent.empty\") return \"intent\";\n if (code === \"scaffold.placeholder\") return subject === undefined ? \"delta\" : \"spec\";\n return null;\n}\n\n/**\n * The artifact table. Every entry is graded by two questions in order — is it\n * there, and does anything the shared checks reported name it — so a file that\n * exists and is wrong can never be reported the way one that was never written\n * is.\n */\n/** What the grading of one feature's files needs beyond the files themselves. */\nexport interface Grading {\n /** The findings that make an artifact `draft` rather than `done`. */\n blocking: Finding[];\n verification: VerificationState;\n /** Operations some OTHER feature's contract already holds. */\n contracted: ReadonlySet<string>;\n /** Operations this feature's own requirements govern. */\n governs: ReadonlySet<string>;\n /** The enumeration's entries by id — where each living service is and what it has (owesContract reads both). */\n living: ReadonlyMap<string, ServiceEntry>;\n /**\n * The capability documents this feature's delta carries. Empty for every\n * feature in a fleet that has not adopted the business axis, which is what\n * keeps the rows out of those payloads entirely.\n */\n capabilities: readonly CapabilityDoc[];\n}\n\nexport function featureArtifacts(\n docsDir: DocsDir,\n feature: FeatureEntry,\n services: readonly PathableService[],\n grading: Grading,\n): ArtifactState[] {\n const { blocking, verification, contracted, governs, living, capabilities } = grading;\n const paths = featurePaths(feature.dir);\n const rel = (abs: string): string => repoPath(docsDir, abs);\n const faults = blocking.map((f) => ({ artifact: faultedArtifact(f.code, f.subject), subject: f.subject }));\n const faulted = (id: ArtifactId, service: string | null): boolean =>\n faults.some((f) => f.artifact === id && (service === null || f.subject === service));\n\n const out: ArtifactState[] = [\n fileState(\"intent\", { service: null, path: rel(paths.intent), exists: existsSync(paths.intent) }, true, faulted(\"intent\", null)),\n fileState(\"delta\", { service: null, path: rel(paths.delta), exists: existsSync(paths.delta) }, false, faulted(\"delta\", null)),\n ];\n\n // One row per capability document the feature carries, and none at all when\n // it carries none: the row is the answer to \"which file do I edit\", and a\n // fleet that has not adopted the axis has no such file to name. Never\n // `required` — no feature owes a business change — so absence never shows up\n // as work owed, and only a fault turns one `draft`. A `delta.*` fault carries\n // the capability id in `subject` with nothing saying it is not a service, so\n // the membership test is the resolution the code family cannot give.\n for (const doc of capabilities) {\n out.push({\n id: \"capabilities\",\n service: null,\n capability: doc.id,\n path: rel(doc.spec),\n exists: existsSync(doc.spec),\n required: false,\n status: faulted(\"capabilities\", doc.id) || faulted(\"spec\", doc.id) ? \"draft\" : \"done\",\n blockedBy: [],\n });\n }\n\n for (const svc of services) {\n const p = featureSpecPaths(feature.dir, svc);\n const specFault = faulted(\"spec\", svc);\n // What a service delta owes is a REQUIREMENT DELTA, not specifically the\n // business one. `arch.spec.md` is the other corpus, with the same grammar\n // and the same delta algebra, and `featureChecklist` derives claims from\n // it — so a directory holding only the architectural document is a\n // complete delta, and every gate already reads it as one: `coherence`'s\n // `service.no-requirement-delta` counts the DIRECTORY, and\n // `validate --feature` grades an arch-only delta clean at exit 0.\n //\n // Requiring `spec.md` unconditionally made this module the one document\n // that disagreed. An architecture-only feature — an outbox added, a retry\n // policy, an alert; the changes `arch.spec.md` exists for because no\n // business scenario was ever going to carry them — reported `missing` and\n // emitted `next.author-spec`, sending the author to write business\n // requirements the change does not have, about a feature the gate accepts.\n // status may be redder than the gates on a question they also ask; it may\n // not invent an obligation they do not.\n const archExists = existsSync(p.archSpec);\n out.push(fileState(\"spec\", { service: svc, path: rel(p.spec), exists: existsSync(p.spec) }, !archExists, specFault));\n out.push(fileState(\"arch-spec\", { service: svc, path: rel(p.archSpec), exists: archExists }, false, specFault));\n out.push(\n fileState(\n \"openapi\",\n { service: svc, path: rel(p.openapi), exists: existsSync(p.openapi) },\n owesContract(living.get(svc), contracted.has(svc), governs.has(svc)),\n faulted(\"openapi\", svc),\n ),\n );\n // Never required: an event contract is genuinely optional — the axis's own\n // absence-grading rests on that — so absence is `done`, exactly like\n // arch.spec.md. Present and faulted still turns the row `draft`.\n out.push(\n fileState(\n \"asyncapi\",\n { service: svc, path: rel(p.asyncapi), exists: existsSync(p.asyncapi) },\n false,\n faulted(\"asyncapi\", svc),\n ),\n );\n }\n\n // The record is the one artifact with a real prerequisite: its checklist is\n // derived from the delta and the per-service deltas, so with none of them\n // written there is nothing to answer — authoring one is impossible rather\n // than merely premature, which is the whole distinction `blocked` carries.\n //\n // `capabilities` is excluded for exactly that reason and not by oversight:\n // `featureChecklist` derives no claim from a capability document — a business\n // promise is realized by service requirements, and those are what the record\n // answers — so counting one as a feeder would report a capability-only\n // feature as ready to verify against a checklist with nothing in it.\n //\n // `arch-spec` used to be excluded beside it, and that failed the same test\n // the comment above states: `featureChecklist` DOES read `arch.spec.md`\n // (`core/verify/checklist.ts`), and `loam verify` on an architecture-only\n // feature returns a real checklist. The row said `blocked … needs delta,\n // spec, openapi, asyncapi` and `next.verify` said there was nothing to derive\n // a checklist from, about a command that derives one — which is the failure\n // `blocked` is supposed to prevent, pointed the wrong way.\n const feeders = out.filter((a) => a.id !== \"intent\" && a.id !== \"capabilities\" && a.exists);\n out.push({\n id: \"verification\",\n service: null,\n path: rel(recordLocation(feature.dir)),\n exists: verification.state !== \"absent\",\n required: true,\n status: feeders.length === 0 ? \"blocked\" : verificationStatus(verification),\n blockedBy: feeders.length === 0 ? [\"delta\", \"spec\", \"arch-spec\", \"openapi\", \"asyncapi\"] : [],\n });\n return out;\n}\n\n/** Where an artifact is and whether it is there — the three facts a grade reads. */\ninterface FileFacts {\n service: string | null;\n path: string;\n exists: boolean;\n}\n\nfunction fileState(id: ArtifactId, file: FileFacts, required: boolean, faulted: boolean): ArtifactState {\n const { service, path, exists } = file;\n const status: ArtifactStatus = !exists\n ? required\n ? \"missing\"\n : \"done\"\n : faulted\n ? \"draft\"\n : \"done\";\n return { id, service, path, exists, required, status, blockedBy: [] };\n}\n"]}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { featureCoherence } from "../../coherence/coherence.js";
|
|
9
9
|
import { invalidSpecServiceFindings, livingMergeConflicts, unknownDeltaServices, } from "../../coherence/living.js";
|
|
10
10
|
import { analyzeDependencies } from "../../dependencies/dependencies.js";
|
|
11
|
+
import { missingScenariosFinding } from "../../document/scenarios.js";
|
|
11
12
|
import { repoPath } from "../../envelope/json.js";
|
|
12
13
|
import { FleetContext } from "../../fleet-context.js";
|
|
13
14
|
import { featureProvenance } from "../../provenance/findings.js";
|
|
@@ -28,8 +29,8 @@ import {} from "./state.js";
|
|
|
28
29
|
* complete either way, because a narrowed view that also hid which other
|
|
29
30
|
* services exist would read as a feature that touches exactly one.
|
|
30
31
|
*/
|
|
31
|
-
export async function featureStatus(docsDir, feature, opts
|
|
32
|
-
const context = opts
|
|
32
|
+
export async function featureStatus(docsDir, feature, opts) {
|
|
33
|
+
const { context } = opts;
|
|
33
34
|
const interrupted = await readInterruptedCommit(docsDir);
|
|
34
35
|
const services = await featureSpecServices(feature.dir, context);
|
|
35
36
|
const narrowed = opts.service;
|
|
@@ -153,15 +154,8 @@ async function featureFindings(docsDir, feature, scans, context) {
|
|
|
153
154
|
const capabilityDeltas = await context.featureCapabilityDeltas(feature.dir);
|
|
154
155
|
out.push(...(await livingMergeConflicts(docsDir, await featureSpecServices(feature.dir, context), capabilityDeltas.docs.map((d) => d.id), context)));
|
|
155
156
|
for (const scan of scans) {
|
|
156
|
-
if (scan.bare.length
|
|
157
|
-
|
|
158
|
-
out.push({
|
|
159
|
-
severity: "error",
|
|
160
|
-
code: "requirements.missing-scenarios",
|
|
161
|
-
subject: scan.service,
|
|
162
|
-
message: `${scan.label}: ${scan.bare.length} requirement(s) without a scenario`,
|
|
163
|
-
details: scan.bare.map((r) => r.name),
|
|
164
|
-
});
|
|
157
|
+
if (scan.bare.length > 0)
|
|
158
|
+
out.push(missingScenariosFinding(scan.label, scan.bare, scan.service));
|
|
165
159
|
}
|
|
166
160
|
return out;
|
|
167
161
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature.js","sourceRoot":"","sources":["../../../../src/core/status/feature/feature.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One feature: the full report. `featureStatus` gathers what the shared gates\n * say about a feature in flight — findings, the artifact table, the record,\n * the dependency order — and rolls them up into the stage and the steps. The\n * vocabulary it reports in lives in ../report.ts, whose header holds the rules\n * this projection is held to.\n */\nimport { featureCoherence } from \"../../coherence/coherence.js\";\nimport {\n invalidSpecServiceFindings,\n livingMergeConflicts,\n unknownDeltaServices,\n} from \"../../coherence/living.js\";\nimport { analyzeDependencies } from \"../../dependencies/dependencies.js\";\nimport { repoPath } from \"../../envelope/json.js\";\nimport { FleetContext } from \"../../fleet-context.js\";\nimport { featureProvenance } from \"../../provenance/findings.js\";\nimport type { FeatureEntry } from \"../../repo/entries.js\";\nimport { featureSpecServices } from \"../../repo/repo.js\";\nimport { enumeratedServices } from \"../../repo/service-target.js\";\nimport { useCaseBlastRadius } from \"../../usecases/touch.js\";\nimport { issueFinding, type Issue } from \"../../vocabulary/issue.js\";\nimport type { Finding } from \"../../vocabulary/report.js\";\nimport { contractOwners, contractsHeldElsewhere } from \"../contracts.js\";\nimport { readInterruptedCommit } from \"../interrupted.js\";\nimport type { ArtifactStatus, FeatureStatusReport } from \"../report.js\";\nimport { governedServices, scanDeltas, type DeltaScan } from \"../scan.js\";\nimport { fullyVerified, verificationState } from \"../verification.js\";\nimport { featureArtifacts } from \"./artifacts.js\";\nimport { featureNext, unshippable } from \"./next.js\";\nimport { type FeatureState } from \"./state.js\";\nimport type { DocsDir } from \"../../kernel/ids/dirs.js\";\n\n/**\n * Everything about one feature in flight. `service` narrows the per-service\n * artifacts and the per-service steps; the feature's own `services` list stays\n * complete either way, because a narrowed view that also hid which other\n * services exist would read as a feature that touches exactly one.\n */\nexport async function featureStatus(\n docsDir: DocsDir,\n feature: FeatureEntry,\n opts: { service?: string; boundService?: string; context?: FleetContext } = {},\n): Promise<FeatureStatusReport> {\n const context = opts.context ?? new FleetContext();\n const interrupted = await readInterruptedCommit(docsDir);\n const services = await featureSpecServices(feature.dir, context);\n const narrowed = opts.service;\n const inView = narrowed === undefined ? services : services.filter((s) => s === narrowed);\n\n const scans = feature.archived ? [] : await scanDeltas(docsDir, feature, services, context);\n const read = await verificationState(docsDir, feature);\n const verification = read.state;\n // An archived feature is history: its delta is already folded into the living\n // docs, so coherence would grade the change against a world that has absorbed\n // it and report contradictions that are only the merge having happened. The\n // same reason `list` never calls an archived record stale.\n const findings = feature.archived ? [] : await featureFindings(docsDir, feature, scans, context);\n // `verify.scenario-attested` rides in the same array an agent already parses,\n // under verify's own code — a feature confirmed the short way has to be\n // MARKED here, not merely absent from `stage: done`. It is a warning and it\n // gates nothing, so `coherent` is untouched.\n if (!feature.archived && read.notice !== null) {\n findings.push({ severity: read.notice.severity, code: read.notice.code, message: read.notice.message });\n }\n const errors = findings.filter((f) => f.severity === \"error\");\n const warnings = findings.filter((f) => f.severity === \"warn\").length;\n // The union of the two gates, and the set every verdict below is drawn from.\n const blocking = findings.filter(unshippable);\n\n const governs = governedServices(scans);\n // The whole feature is graded, then the view is narrowed — never the other\n // way round. `--service` is a lens on the table and the steps; a rollup that\n // moved with it would report a feature `ready` because the one service you\n // asked about happens to be written, while three others have nothing.\n const living = new Map((await enumeratedServices(docsDir, context)).map((s) => [s.id, s]));\n const graded = featureArtifacts(docsDir, feature, services, {\n blocking,\n verification,\n contracted: contractsHeldElsewhere(await contractOwners(docsDir, context), feature.id),\n governs,\n // Tolerant on purpose: status must keep answering in a docs repo whose\n // services/ is broken or missing — an unenumerable fleet is doctor's\n // diagnosis, and here it simply means every service reads as never-adopted.\n living,\n // Through the context's memo — the same walk `featureFindings` above and\n // the coherence pass inside it already ran. An archived feature carries no\n // delta to name, and the walk is one `existsSync` besides.\n capabilities: (await context.featureCapabilityDeltas(feature.dir)).docs,\n });\n const artifacts =\n narrowed === undefined ? graded : graded.filter((a) => a.service === null || a.service === narrowed);\n\n // The dependency index rather than the `*-pending` issue codes: those name the\n // other feature in prose, and prose is not an API — dependencies.ts opens with\n // exactly that argument. An archived feature waits on nothing; it landed.\n const graph = feature.archived\n ? null\n : await analyzeDependencies(docsDir, feature.id, context);\n const blockedBy = graph?.nodes.find((n) => n.id === feature.id)?.dependsOn ?? [];\n\n // The flows the services in view are already hops of. The fleet set is the\n // living services UNION the feature's own, so a service this feature\n // introduces resolves to itself rather than to a container's title — and the\n // load underneath gates itself, so a docs repo that declares no use case pays\n // a readdir here rather than a LikeC4 workspace.\n const useCases = await useCaseBlastRadius({\n docsDir,\n known: new Set([...living.keys(), ...services]),\n services: new Set(inView),\n });\n\n return {\n interrupted,\n feature: {\n id: feature.id,\n dirName: feature.dirName,\n path: repoPath(docsDir, feature.dir),\n archived: feature.archived,\n stage: featureStage(\n { feature, services, artifacts: graded, findings, blockedBy, verification, scans, interrupted },\n blocking.length,\n ),\n services,\n blockedBy,\n },\n service: narrowed ?? null,\n artifacts,\n checks: {\n ran: !feature.archived,\n coherent: errors.length === 0,\n errors: errors.length,\n warnings,\n gating: findings.filter((f) => f.gates === true).length,\n issues: findings,\n },\n verification,\n useCases,\n next: featureNext(\n { feature, services: inView, artifacts, findings, blockedBy, verification, scans, interrupted },\n opts.boundService,\n ),\n };\n}\n\n/**\n * Everything `loam validate --feature` and `loam archive` would say about this\n * feature, in one list, through the very functions they call.\n *\n * Coherence alone was not it. `validate --feature` also runs `featureProvenance`\n * over intent.md and the scenario-coverage check over every per-service delta,\n * and both grade ERRORS — so a feature whose requirements carry no scenario at\n * all read `coherent: true` here while `validate` exited 1 on the same tree.\n * The coherence findings carry `gates` resolved, because archive's question is\n * not severity's (issue.ts) and no consumer should re-derive it.\n */\nasync function featureFindings(\n docsDir: DocsDir,\n feature: FeatureEntry,\n scans: DeltaScan[],\n context: FleetContext,\n): Promise<Finding[]> {\n const issues: Issue[] = await featureCoherence({ docsDir, featureDir: feature.dir, featureId: feature.id, context });\n const out: Finding[] = issues.map(issueFinding);\n out.push(...(await featureProvenance(feature.dir, feature.id)).filter((f) => f.severity !== \"ok\"));\n // The three refusals `archive` makes before its merge plan runs, from the\n // same functions it calls: a per-service delta addressed to a service that\n // exists nowhere, a specs/ directory whose NAME the id grammar refuses\n // (that one is refused before archive plans anything — the name itself is\n // the path it would become), and a LIVING document still holding git\n // conflict markers. None are coherence issues — one is about a service id\n // that means nothing, one about a name that can never be a path, one about\n // a LIVING document nobody wrote — so they reach archive outside\n // `featureCoherence`, and a projection that only inherited coherence\n // printed \"ship it\" over all three. Errors: each is a refusal, and the rule\n // this module is held to is one-directional.\n out.push(...(await unknownDeltaServices(docsDir, feature.dir, feature.id, { context })));\n out.push(...(await invalidSpecServiceFindings(feature.dir, context)));\n // The capability half of the same scan: status may be redder than the gates\n // and never greener, so a living capability document this feature's delta\n // would rewrite has to be looked at here too. Through the context's memo —\n // this function always has one — so the walk is shared with the coherence\n // pass above rather than repeated.\n const capabilityDeltas = await context.featureCapabilityDeltas(feature.dir);\n out.push(\n ...(await livingMergeConflicts(docsDir, await featureSpecServices(feature.dir, context), capabilityDeltas.docs.map((d) => d.id), context)),\n );\n for (const scan of scans) {\n if (scan.bare.length === 0) continue;\n out.push({\n severity: \"error\",\n code: \"requirements.missing-scenarios\",\n subject: scan.service,\n message: `${scan.label}: ${scan.bare.length} requirement(s) without a scenario`,\n details: scan.bare.map((r) => r.name),\n });\n }\n return out;\n}\n\n/**\n * The feature's own rollup, worst first. The order is the order the work has to\n * happen in, so the winning branch is also the phase the feature is in:\n * unwritten artifacts come before an ordering problem (you can author while\n * another feature is still in flight), an ordering problem comes before this\n * feature's own errors (fixing them may be pointless until the other one\n * lands), and everything comes before the record — answers written over a\n * broken feature are answers to questions that are about to change.\n *\n * The record is excluded from the `missing` branch on purpose. It is not\n * something an author writes: it is the done-check's output, and counting its\n * absence as unwritten work would report every correctly-authored feature as\n * `missing` right up until somebody ran `loam verify` — which is the state\n * `ready` exists to name, and which the fleet form already names correctly.\n */\nfunction featureStage(state: FeatureState, blocking: number): ArtifactStatus {\n const { feature, artifacts, services, blockedBy, verification, interrupted } = state;\n // Ahead of `archived`, and ahead of everything else: a killed commit may be\n // this very feature's, half-applied, so \"it shipped\" is a claim about files\n // nobody has established the contents of. `blocked` is the honest word — the\n // work is stalled on something that is not this feature's artifact — and it\n // is what stops the projection printing `done` over a half-merged repo.\n if (interrupted !== null) return \"blocked\";\n if (feature.archived) return \"done\";\n // A feature with no `specs/<service>/` changes nothing. There is no artifact\n // row to be missing — the rows are per service — so the emptiness itself is\n // the finding, and `next.touch-service` is the step.\n if (services.length === 0) return \"missing\";\n if (artifacts.some((a) => a.id !== \"verification\" && a.status === \"missing\")) return \"missing\";\n if (blockedBy.length > 0) return \"blocked\";\n if (blocking > 0) return \"draft\";\n return fullyVerified(verification) ? \"done\" : \"ready\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"feature.js","sourceRoot":"","sources":["../../../../src/core/status/feature/feature.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One feature: the full report. `featureStatus` gathers what the shared gates\n * say about a feature in flight — findings, the artifact table, the record,\n * the dependency order — and rolls them up into the stage and the steps. The\n * vocabulary it reports in lives in ../report.ts, whose header holds the rules\n * this projection is held to.\n */\nimport { featureCoherence } from \"../../coherence/coherence.js\";\nimport {\n invalidSpecServiceFindings,\n livingMergeConflicts,\n unknownDeltaServices,\n} from \"../../coherence/living.js\";\nimport { analyzeDependencies } from \"../../dependencies/dependencies.js\";\nimport { missingScenariosFinding } from \"../../document/scenarios.js\";\nimport { repoPath } from \"../../envelope/json.js\";\nimport { FleetContext } from \"../../fleet-context.js\";\nimport { featureProvenance } from \"../../provenance/findings.js\";\nimport type { FeatureEntry } from \"../../repo/entries.js\";\nimport { featureSpecServices } from \"../../repo/repo.js\";\nimport { enumeratedServices } from \"../../repo/service-target.js\";\nimport { useCaseBlastRadius } from \"../../usecases/touch.js\";\nimport { issueFinding, type Issue } from \"../../vocabulary/issue.js\";\nimport type { Finding } from \"../../vocabulary/report.js\";\nimport { contractOwners, contractsHeldElsewhere } from \"../contracts.js\";\nimport { readInterruptedCommit } from \"../interrupted.js\";\nimport type { ArtifactStatus, FeatureStatusReport } from \"../report.js\";\nimport { governedServices, scanDeltas, type DeltaScan } from \"../scan.js\";\nimport { fullyVerified, verificationState } from \"../verification.js\";\nimport { featureArtifacts } from \"./artifacts.js\";\nimport { featureNext, unshippable } from \"./next.js\";\nimport { type FeatureState } from \"./state.js\";\nimport type { DocsDir } from \"../../kernel/ids/dirs.js\";\n\n/**\n * Everything about one feature in flight. `service` narrows the per-service\n * artifacts and the per-service steps; the feature's own `services` list stays\n * complete either way, because a narrowed view that also hid which other\n * services exist would read as a feature that touches exactly one.\n */\nexport async function featureStatus(\n docsDir: DocsDir,\n feature: FeatureEntry,\n opts: { service?: string; boundService?: string; context: FleetContext },\n): Promise<FeatureStatusReport> {\n const { context } = opts;\n const interrupted = await readInterruptedCommit(docsDir);\n const services = await featureSpecServices(feature.dir, context);\n const narrowed = opts.service;\n const inView = narrowed === undefined ? services : services.filter((s) => s === narrowed);\n\n const scans = feature.archived ? [] : await scanDeltas(docsDir, feature, services, context);\n const read = await verificationState(docsDir, feature);\n const verification = read.state;\n // An archived feature is history: its delta is already folded into the living\n // docs, so coherence would grade the change against a world that has absorbed\n // it and report contradictions that are only the merge having happened. The\n // same reason `list` never calls an archived record stale.\n const findings = feature.archived ? [] : await featureFindings(docsDir, feature, scans, context);\n // `verify.scenario-attested` rides in the same array an agent already parses,\n // under verify's own code — a feature confirmed the short way has to be\n // MARKED here, not merely absent from `stage: done`. It is a warning and it\n // gates nothing, so `coherent` is untouched.\n if (!feature.archived && read.notice !== null) {\n findings.push({ severity: read.notice.severity, code: read.notice.code, message: read.notice.message });\n }\n const errors = findings.filter((f) => f.severity === \"error\");\n const warnings = findings.filter((f) => f.severity === \"warn\").length;\n // The union of the two gates, and the set every verdict below is drawn from.\n const blocking = findings.filter(unshippable);\n\n const governs = governedServices(scans);\n // The whole feature is graded, then the view is narrowed — never the other\n // way round. `--service` is a lens on the table and the steps; a rollup that\n // moved with it would report a feature `ready` because the one service you\n // asked about happens to be written, while three others have nothing.\n const living = new Map((await enumeratedServices(docsDir, context)).map((s) => [s.id, s]));\n const graded = featureArtifacts(docsDir, feature, services, {\n blocking,\n verification,\n contracted: contractsHeldElsewhere(await contractOwners(docsDir, context), feature.id),\n governs,\n // Tolerant on purpose: status must keep answering in a docs repo whose\n // services/ is broken or missing — an unenumerable fleet is doctor's\n // diagnosis, and here it simply means every service reads as never-adopted.\n living,\n // Through the context's memo — the same walk `featureFindings` above and\n // the coherence pass inside it already ran. An archived feature carries no\n // delta to name, and the walk is one `existsSync` besides.\n capabilities: (await context.featureCapabilityDeltas(feature.dir)).docs,\n });\n const artifacts =\n narrowed === undefined ? graded : graded.filter((a) => a.service === null || a.service === narrowed);\n\n // The dependency index rather than the `*-pending` issue codes: those name the\n // other feature in prose, and prose is not an API — dependencies.ts opens with\n // exactly that argument. An archived feature waits on nothing; it landed.\n const graph = feature.archived\n ? null\n : await analyzeDependencies(docsDir, feature.id, context);\n const blockedBy = graph?.nodes.find((n) => n.id === feature.id)?.dependsOn ?? [];\n\n // The flows the services in view are already hops of. The fleet set is the\n // living services UNION the feature's own, so a service this feature\n // introduces resolves to itself rather than to a container's title — and the\n // load underneath gates itself, so a docs repo that declares no use case pays\n // a readdir here rather than a LikeC4 workspace.\n const useCases = await useCaseBlastRadius({\n docsDir,\n known: new Set([...living.keys(), ...services]),\n services: new Set(inView),\n });\n\n return {\n interrupted,\n feature: {\n id: feature.id,\n dirName: feature.dirName,\n path: repoPath(docsDir, feature.dir),\n archived: feature.archived,\n stage: featureStage(\n { feature, services, artifacts: graded, findings, blockedBy, verification, scans, interrupted },\n blocking.length,\n ),\n services,\n blockedBy,\n },\n service: narrowed ?? null,\n artifacts,\n checks: {\n ran: !feature.archived,\n coherent: errors.length === 0,\n errors: errors.length,\n warnings,\n gating: findings.filter((f) => f.gates === true).length,\n issues: findings,\n },\n verification,\n useCases,\n next: featureNext(\n { feature, services: inView, artifacts, findings, blockedBy, verification, scans, interrupted },\n opts.boundService,\n ),\n };\n}\n\n/**\n * Everything `loam validate --feature` and `loam archive` would say about this\n * feature, in one list, through the very functions they call.\n *\n * Coherence alone was not it. `validate --feature` also runs `featureProvenance`\n * over intent.md and the scenario-coverage check over every per-service delta,\n * and both grade ERRORS — so a feature whose requirements carry no scenario at\n * all read `coherent: true` here while `validate` exited 1 on the same tree.\n * The coherence findings carry `gates` resolved, because archive's question is\n * not severity's (issue.ts) and no consumer should re-derive it.\n */\nasync function featureFindings(\n docsDir: DocsDir,\n feature: FeatureEntry,\n scans: DeltaScan[],\n context: FleetContext,\n): Promise<Finding[]> {\n const issues: Issue[] = await featureCoherence({ docsDir, featureDir: feature.dir, featureId: feature.id, context });\n const out: Finding[] = issues.map(issueFinding);\n out.push(...(await featureProvenance(feature.dir, feature.id)).filter((f) => f.severity !== \"ok\"));\n // The three refusals `archive` makes before its merge plan runs, from the\n // same functions it calls: a per-service delta addressed to a service that\n // exists nowhere, a specs/ directory whose NAME the id grammar refuses\n // (that one is refused before archive plans anything — the name itself is\n // the path it would become), and a LIVING document still holding git\n // conflict markers. None are coherence issues — one is about a service id\n // that means nothing, one about a name that can never be a path, one about\n // a LIVING document nobody wrote — so they reach archive outside\n // `featureCoherence`, and a projection that only inherited coherence\n // printed \"ship it\" over all three. Errors: each is a refusal, and the rule\n // this module is held to is one-directional.\n out.push(...(await unknownDeltaServices(docsDir, feature.dir, feature.id, { context })));\n out.push(...(await invalidSpecServiceFindings(feature.dir, context)));\n // The capability half of the same scan: status may be redder than the gates\n // and never greener, so a living capability document this feature's delta\n // would rewrite has to be looked at here too. Through the context's memo —\n // this function always has one — so the walk is shared with the coherence\n // pass above rather than repeated.\n const capabilityDeltas = await context.featureCapabilityDeltas(feature.dir);\n out.push(\n ...(await livingMergeConflicts(docsDir, await featureSpecServices(feature.dir, context), capabilityDeltas.docs.map((d) => d.id), context)),\n );\n for (const scan of scans) {\n if (scan.bare.length > 0) out.push(missingScenariosFinding(scan.label, scan.bare, scan.service));\n }\n return out;\n}\n\n/**\n * The feature's own rollup, worst first. The order is the order the work has to\n * happen in, so the winning branch is also the phase the feature is in:\n * unwritten artifacts come before an ordering problem (you can author while\n * another feature is still in flight), an ordering problem comes before this\n * feature's own errors (fixing them may be pointless until the other one\n * lands), and everything comes before the record — answers written over a\n * broken feature are answers to questions that are about to change.\n *\n * The record is excluded from the `missing` branch on purpose. It is not\n * something an author writes: it is the done-check's output, and counting its\n * absence as unwritten work would report every correctly-authored feature as\n * `missing` right up until somebody ran `loam verify` — which is the state\n * `ready` exists to name, and which the fleet form already names correctly.\n */\nfunction featureStage(state: FeatureState, blocking: number): ArtifactStatus {\n const { feature, artifacts, services, blockedBy, verification, interrupted } = state;\n // Ahead of `archived`, and ahead of everything else: a killed commit may be\n // this very feature's, half-applied, so \"it shipped\" is a claim about files\n // nobody has established the contents of. `blocked` is the honest word — the\n // work is stalled on something that is not this feature's artifact — and it\n // is what stops the projection printing `done` over a half-merged repo.\n if (interrupted !== null) return \"blocked\";\n if (feature.archived) return \"done\";\n // A feature with no `specs/<service>/` changes nothing. There is no artifact\n // row to be missing — the rows are per service — so the emptiness itself is\n // the finding, and `next.touch-service` is the step.\n if (services.length === 0) return \"missing\";\n if (artifacts.some((a) => a.id !== \"verification\" && a.status === \"missing\")) return \"missing\";\n if (blockedBy.length > 0) return \"blocked\";\n if (blocking > 0) return \"draft\";\n return fullyVerified(verification) ? \"done\" : \"ready\";\n}\n"]}
|
|
@@ -33,8 +33,8 @@ import { fleetNext } from "./next.js";
|
|
|
33
33
|
* are drawn from artifact presence and from a record that already exists, and
|
|
34
34
|
* `next[]` hands off to `loam status <FEAT>` for everything else.
|
|
35
35
|
*/
|
|
36
|
-
export async function fleetStatus(docsDir, opts
|
|
37
|
-
const context = opts
|
|
36
|
+
export async function fleetStatus(docsDir, opts) {
|
|
37
|
+
const { context } = opts;
|
|
38
38
|
const interrupted = await readInterruptedCommit(docsDir);
|
|
39
39
|
const narrowed = opts.service;
|
|
40
40
|
const all = await listServices(docsDir, context);
|
|
@@ -88,6 +88,10 @@ export async function fleetStatus(docsDir, opts = {}) {
|
|
|
88
88
|
// headline, and "118 vouched" over a fleet where 90 of them were read
|
|
89
89
|
// from a sample is the misreading the scope exists to prevent.
|
|
90
90
|
sampledVouched: services.filter((s) => vouched(s) && s.vouchScope === "sampled").length,
|
|
91
|
+
// The other subset, for the same reason: "118 vouched" over a fleet where
|
|
92
|
+
// twenty documents were edited after the person read them is the
|
|
93
|
+
// trust headline lying by omission.
|
|
94
|
+
contentStale: services.filter((s) => vouched(s) && s.contentStale.length > 0).length,
|
|
91
95
|
},
|
|
92
96
|
features,
|
|
93
97
|
order: graph.order.filter((id) => features.some((f) => f.id === id)),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleet.js","sourceRoot":"","sources":["../../../../src/core/status/fleet/fleet.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The whole repository — why the fleet form is cheaper than the feature form.\n *\n * `loam status` with no feature runs over every active feature, and deriving a\n * coherence report or a verification checklist per feature means spinning a\n * fresh LikeC4 workspace per feature. On a fleet-sized repository that is the\n * cost that gets a command switched off — `list` made the same call about its\n * verification column. So the fleet form grades nothing: it reports artifact\n * PRESENCE, the dependency order, and a verification record only where one\n * already exists, and its `next[]` hands off to `loam status <FEAT>`, which is\n * where the checks actually run.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { analyzeDependencies } from \"../../dependencies/dependencies.js\";\nimport { type DependencyGraph } from \"../../dependencies/facts.js\";\nimport { isLandscapeStub } from \"../../scaffold/landscape.js\";\nimport { repoPath } from \"../../envelope/json.js\";\nimport { FleetContext } from \"../../fleet-context.js\";\nimport { compareIds, type FeatureEntry, type ServiceEntry } from \"../../repo/entries.js\";\nimport { featurePaths, featureSpecPaths, landscapePath } from \"../../repo/paths.js\";\nimport { listFeatures, listServices } from \"../../repo/repo.js\";\nimport { contractOwners, contractsHeldElsewhere, owesContract } from \"../contracts.js\";\nimport { readInterruptedCommit } from \"../interrupted.js\";\nimport {\n undocumented,\n vouched,\n type ArtifactStatus,\n type FleetFeatureState,\n type FleetStatusReport,\n type InterruptedCommit,\n} from \"../report.js\";\nimport { governedServices, scanDeltas } from \"../scan.js\";\nimport { fullyVerified, verificationState } from \"../verification.js\";\nimport { fleetNext, type Binding } from \"./next.js\";\nimport type { DocsDir } from \"../../kernel/ids/dirs.js\";\n\n/**\n * The fleet view: how much of `services/` anyone has written down, every\n * feature in flight with the stage it has reached, and the order they have to\n * land in. Nothing here is graded — the module header says why — so the stages\n * are drawn from artifact presence and from a record that already exists, and\n * `next[]` hands off to `loam status <FEAT>` for everything else.\n */\nexport async function fleetStatus(\n docsDir: DocsDir,\n opts: { service?: string; bound?: string; context?: FleetContext } = {},\n): Promise<FleetStatusReport> {\n const context = opts.context ?? new FleetContext();\n const interrupted = await readInterruptedCommit(docsDir);\n const narrowed = opts.service;\n const all = await listServices(docsDir, context);\n const services = narrowed === undefined ? all : all.filter((s) => s.id === narrowed);\n // The service this repository says it IS, and whether the fleet has heard of\n // it. Asked against the UNNARROWED list, because \"is it adopted\" is a question\n // about the fleet and not about the view.\n //\n // Both facts travel, where only the unadopted case used to. This value was a\n // `string | null` that went null exactly when the service directory EXISTED,\n // so the walk could see the repository it stands in only while that\n // repository was undocumented — and a developer in an adopted service repo\n // got the whole fleet's worklist with their own boundary buried in it.\n const binding: Binding | null =\n opts.bound === undefined ? null : { id: opts.bound, adopted: all.some((s) => s.id === opts.bound) };\n const graph = await analyzeDependencies(docsDir, undefined, context);\n const entries = await listFeatures(docsDir, {}, context);\n const inScope = narrowed === undefined ? entries : entries.filter((f) => f.services.some((s) => s === narrowed));\n // Once for the fleet, not once per feature: which features hold a contract\n // for which service is one fact about the repository, and every feature below\n // reads its own row out of it. Built over the UNNARROWED list on purpose — a\n // `--service` view still has to know that a feature outside it discharges the\n // contract, or the narrowing would invent an obligation.\n const owners = await contractOwners(docsDir, context);\n\n const features = await Promise.all(\n inScope.map((f) =>\n fleetFeature(docsDir, f, {\n graph,\n contracted: contractsHeldElsewhere(owners, f.id),\n living: new Map(all.map((s) => [s.id, s])),\n context,\n interrupted,\n }),\n ),\n );\n features.sort((a, b) => compareIds(a.id, b.id));\n\n // The first-hour ladder's fact, computed exactly when the ladder can apply\n // — an unnarrowed run over a fleet with zero services and zero features —\n // so the read disappears from every repository that has anything in it.\n // Withheld on narrowed runs for the same reason the binding is withheld\n // below: `--service X` is an explicit question about X, and the\n // repository's onboarding ladder would be a different question's answer.\n const teaching =\n narrowed === undefined && services.length === 0 && features.length === 0\n ? { landscape: await landscapeTeaching(docsDir) }\n : null;\n\n return {\n interrupted,\n services: {\n total: services.length,\n undocumented: services.filter(undocumented).length,\n draft: services.filter((s) => !undocumented(s) && !vouched(s)).length,\n vouched: services.filter(vouched).length,\n // A subset of `vouched`, never a fourth bucket: those services ARE\n // vouched, and the three counts still sum to `total`. It rides beside\n // the count it qualifies because this line is the fleet's trust\n // headline, and \"118 vouched\" over a fleet where 90 of them were read\n // from a sample is the misreading the scope exists to prevent.\n sampledVouched: services.filter((s) => vouched(s) && s.vouchScope === \"sampled\").length,\n },\n features,\n order: graph.order.filter((id) => features.some((f) => f.id === id)),\n service: narrowed ?? null,\n // The binding is passed only when this run was NOT narrowed, and that guard\n // is unchanged now that the binding also decides ORDER. `--service X` is an\n // explicit question about X, and answering it with a step about whichever\n // service loam.json happens to name would be a different question's answer\n // at the top of the list — which is as true of a rung promoted to the front\n // as it was of `next.adopt-bound` emitted out of nowhere. A narrowed run\n // sees at most the one service it asked about anyway, so withholding the\n // binding here costs the reader nothing and keeps `--service X` answering\n // only about X.\n next: fleetNext(\n { services, features, graph, interrupted, teaching },\n narrowed === undefined ? binding : null,\n ),\n };\n}\n\n/**\n * Which first-hour state the fleet map is in: absent, still the scaffold's own\n * untouched bytes, or authored. One file read and a byte comparison — the\n * module header's cost rule stands, no LikeC4 workspace is spun here.\n */\nasync function landscapeTeaching(docsDir: DocsDir): Promise<\"missing\" | \"stub\" | \"authored\"> {\n const lp = landscapePath(docsDir);\n if (!existsSync(lp)) return \"missing\";\n try {\n return isLandscapeStub(await readFile(lp, \"utf8\")) ? \"stub\" : \"authored\";\n } catch {\n // Unreadable is not untouched. A map that cannot be read — a directory in\n // the file's place, a permission — must not be called the scaffold's stub\n // (\"missing\" and \"stub\" would each put a false statement in next[]), so it\n // reads as \"authored\", which only drops the one teaching step: the gate\n // entry still names `loam validate --all`, where an unreadable landscape\n // is actually graded, and letting this read throw would hand the whole\n // orientation surface — a command that reports and never gates — to the\n // repository-unavailable refusal over a file status does not even parse.\n return \"authored\";\n }\n}\n\n/** What a feature's fleet row needs about the fleet AROUND it. */\ninterface FleetView {\n graph: DependencyGraph;\n /** Operations some other feature's contract already holds. */\n contracted: ReadonlySet<string>;\n /** The enumeration's entries by id — where each living service is and what it has (owesContract reads both). */\n living: ReadonlyMap<string, ServiceEntry>;\n context: FleetContext;\n interrupted: InterruptedCommit | null;\n}\n\nasync function fleetFeature(\n docsDir: DocsDir,\n feature: FeatureEntry,\n view: FleetView,\n): Promise<FleetFeatureState> {\n const { graph, contracted, living, context, interrupted } = view;\n const paths = featurePaths(feature.dir);\n const missing: string[] = [];\n if (!existsSync(paths.intent)) missing.push(\"intent\");\n if (feature.services.length === 0) missing.push(\"spec\");\n // Requirement text, not a coherence run: `Operations:` lines are read off the\n // same cached parse `list` already pays for, and the contract question needs\n // them (owesContract). No LikeC4 workspace is spun here, which is the cost\n // this form actually refuses.\n const governs = governedServices(await scanDeltas(docsDir, feature, feature.services, context));\n for (const svc of feature.services) {\n const p = featureSpecPaths(feature.dir, svc);\n // EITHER corpus satisfies it. What a service delta owes is a requirement\n // delta, and `arch.spec.md` is the other one — same grammar, same delta\n // algebra, read by `featureChecklist` — so an architecture-only feature is\n // complete and every gate already says so. This form asked for `spec.md`\n // specifically, which broke the rule the comment two lines down states\n // about the contract question: the two forms disagreed, `loam status`\n // reporting `missing <svc>/spec` over a feature `loam status <FEAT>` and\n // `loam validate --feature` both called complete. The name stays\n // `<svc>/spec` when NEITHER exists, because the business delta is the one\n // an author writes by default.\n if (!existsSync(p.spec) && !existsSync(p.archSpec)) missing.push(`${svc}/spec`);\n // The same question featureArtifacts asks, through the same function: the\n // two forms must never disagree about whether a feature owes a contract.\n if (owesContract(living.get(svc), contracted.has(svc), governs.has(svc)) && !existsSync(p.openapi)) {\n missing.push(`${svc}/openapi`);\n }\n }\n const verification = (await verificationState(docsDir, feature)).state;\n const blockedBy = graph.nodes.find((n) => n.id === feature.id)?.dependsOn ?? [];\n\n // No `draft` here, ever: that verdict needs the coherence run this form\n // refuses to pay for, and inferring it from presence alone would be a second\n // opinion about validity — the one thing status must never invent.\n //\n // The interrupted commit leads, exactly as it does in the feature form: every\n // presence test below is a question about files a killed `archive` may have\n // half-written, so answering `done` from them is a claim about bytes nobody\n // has established. Without this the two forms contradicted each other over\n // one repository — `loam status --json` said `done` and offered \"ship it\"\n // while `loam status <FEAT>` said `blocked` — and the rule in report.ts's\n // header is that this projection is never greener than the gates.\n const stage: ArtifactStatus =\n interrupted !== null\n ? \"blocked\"\n : missing.length > 0\n ? \"missing\"\n : blockedBy.length > 0\n ? \"blocked\"\n : fullyVerified(verification)\n ? \"done\"\n : \"ready\";\n\n return {\n id: feature.id,\n dirName: feature.dirName,\n path: repoPath(docsDir, feature.dir),\n stage,\n services: feature.services,\n blockedBy,\n missing,\n verification,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fleet.js","sourceRoot":"","sources":["../../../../src/core/status/fleet/fleet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAwB,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAwC,MAAM,uBAAuB,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EACL,YAAY,EACZ,OAAO,GAKR,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,SAAS,EAAgB,MAAM,WAAW,CAAC;AAGpD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,OAAgB,EAChB,IAAiE;IAEjE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACzB,MAAM,WAAW,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC;IAC9B,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;IACrF,6EAA6E;IAC7E,+EAA+E;IAC/E,0CAA0C;IAC1C,EAAE;IACF,6EAA6E;IAC7E,6EAA6E;IAC7E,oEAAoE;IACpE,2EAA2E;IAC3E,uEAAuE;IACvE,MAAM,OAAO,GACX,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;IACtG,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC;IACjH,2EAA2E;IAC3E,8EAA8E;IAC9E,6EAA6E;IAC7E,8EAA8E;IAC9E,yDAAyD;IACzD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAEtD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAChB,YAAY,CAAC,OAAO,EAAE,CAAC,EAAE;QACvB,KAAK;QACL,UAAU,EAAE,sBAAsB,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC;QAChD,MAAM,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1C,OAAO;QACP,WAAW;KACZ,CAAC,CACH,CACF,CAAC;IACF,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEhD,2EAA2E;IAC3E,0EAA0E;IAC1E,wEAAwE;IACxE,wEAAwE;IACxE,gEAAgE;IAChE,yEAAyE;IACzE,MAAM,QAAQ,GACZ,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QACtE,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC,OAAO,CAAC,EAAE;QACjD,CAAC,CAAC,IAAI,CAAC;IAEX,OAAO;QACL,WAAW;QACX,QAAQ,EAAE;YACR,KAAK,EAAE,QAAQ,CAAC,MAAM;YACtB,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM;YAClD,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM;YACrE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM;YACxC,mEAAmE;YACnE,sEAAsE;YACtE,gEAAgE;YAChE,sEAAsE;YACtE,+DAA+D;YAC/D,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,MAAM;YACvF,0EAA0E;YAC1E,iEAAiE;YACjE,oCAAoC;YACpC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM;SACrF;QACD,QAAQ;QACR,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACpE,OAAO,EAAE,QAAQ,IAAI,IAAI;QACzB,4EAA4E;QAC5E,4EAA4E;QAC5E,0EAA0E;QAC1E,2EAA2E;QAC3E,4EAA4E;QAC5E,yEAAyE;QACzE,yEAAyE;QACzE,0EAA0E;QAC1E,gBAAgB;QAChB,IAAI,EAAE,SAAS,CACb,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,EACpD,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CACxC;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,iBAAiB,CAAC,OAAgB;IAC/C,MAAM,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAAE,OAAO,SAAS,CAAC;IACtC,IAAI,CAAC;QACH,OAAO,eAAe,CAAC,MAAM,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC;IAC3E,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;QAC1E,0EAA0E;QAC1E,2EAA2E;QAC3E,wEAAwE;QACxE,yEAAyE;QACzE,uEAAuE;QACvE,wEAAwE;QACxE,yEAAyE;QACzE,OAAO,UAAU,CAAC;IACpB,CAAC;AACH,CAAC;AAaD,KAAK,UAAU,YAAY,CACzB,OAAgB,EAChB,OAAqB,EACrB,IAAe;IAEf,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IACjE,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtD,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxD,8EAA8E;IAC9E,6EAA6E;IAC7E,2EAA2E;IAC3E,8BAA8B;IAC9B,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IAChG,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACnC,MAAM,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC7C,yEAAyE;QACzE,wEAAwE;QACxE,2EAA2E;QAC3E,yEAAyE;QACzE,uEAAuE;QACvE,sEAAsE;QACtE,yEAAyE;QACzE,iEAAiE;QACjE,0EAA0E;QAC1E,+BAA+B;QAC/B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,CAAC;QAChF,0EAA0E;QAC1E,yEAAyE;QACzE,IAAI,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;YACnG,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,UAAU,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IACD,MAAM,YAAY,GAAG,CAAC,MAAM,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IACvE,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;IAEhF,wEAAwE;IACxE,6EAA6E;IAC7E,mEAAmE;IACnE,EAAE;IACF,8EAA8E;IAC9E,4EAA4E;IAC5E,4EAA4E;IAC5E,2EAA2E;IAC3E,0EAA0E;IAC1E,0EAA0E;IAC1E,kEAAkE;IAClE,MAAM,KAAK,GACT,WAAW,KAAK,IAAI;QAClB,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YAClB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;gBACpB,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,aAAa,CAAC,YAAY,CAAC;oBAC3B,CAAC,CAAC,MAAM;oBACR,CAAC,CAAC,OAAO,CAAC;IAEpB,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC;QACpC,KAAK;QACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,SAAS;QACT,OAAO;QACP,YAAY;KACb,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The whole repository — why the fleet form is cheaper than the feature form.\n *\n * `loam status` with no feature runs over every active feature, and deriving a\n * coherence report or a verification checklist per feature means spinning a\n * fresh LikeC4 workspace per feature. On a fleet-sized repository that is the\n * cost that gets a command switched off — `list` made the same call about its\n * verification column. So the fleet form grades nothing: it reports artifact\n * PRESENCE, the dependency order, and a verification record only where one\n * already exists, and its `next[]` hands off to `loam status <FEAT>`, which is\n * where the checks actually run.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { analyzeDependencies } from \"../../dependencies/dependencies.js\";\nimport { type DependencyGraph } from \"../../dependencies/facts.js\";\nimport { isLandscapeStub } from \"../../scaffold/landscape.js\";\nimport { repoPath } from \"../../envelope/json.js\";\nimport { FleetContext } from \"../../fleet-context.js\";\nimport { compareIds, type FeatureEntry, type ServiceEntry } from \"../../repo/entries.js\";\nimport { featurePaths, featureSpecPaths, landscapePath } from \"../../repo/paths.js\";\nimport { listFeatures, listServices } from \"../../repo/repo.js\";\nimport { contractOwners, contractsHeldElsewhere, owesContract } from \"../contracts.js\";\nimport { readInterruptedCommit } from \"../interrupted.js\";\nimport {\n undocumented,\n vouched,\n type ArtifactStatus,\n type FleetFeatureState,\n type FleetStatusReport,\n type InterruptedCommit,\n} from \"../report.js\";\nimport { governedServices, scanDeltas } from \"../scan.js\";\nimport { fullyVerified, verificationState } from \"../verification.js\";\nimport { fleetNext, type Binding } from \"./next.js\";\nimport type { DocsDir } from \"../../kernel/ids/dirs.js\";\n\n/**\n * The fleet view: how much of `services/` anyone has written down, every\n * feature in flight with the stage it has reached, and the order they have to\n * land in. Nothing here is graded — the module header says why — so the stages\n * are drawn from artifact presence and from a record that already exists, and\n * `next[]` hands off to `loam status <FEAT>` for everything else.\n */\nexport async function fleetStatus(\n docsDir: DocsDir,\n opts: { service?: string; bound?: string; context: FleetContext },\n): Promise<FleetStatusReport> {\n const { context } = opts;\n const interrupted = await readInterruptedCommit(docsDir);\n const narrowed = opts.service;\n const all = await listServices(docsDir, context);\n const services = narrowed === undefined ? all : all.filter((s) => s.id === narrowed);\n // The service this repository says it IS, and whether the fleet has heard of\n // it. Asked against the UNNARROWED list, because \"is it adopted\" is a question\n // about the fleet and not about the view.\n //\n // Both facts travel, where only the unadopted case used to. This value was a\n // `string | null` that went null exactly when the service directory EXISTED,\n // so the walk could see the repository it stands in only while that\n // repository was undocumented — and a developer in an adopted service repo\n // got the whole fleet's worklist with their own boundary buried in it.\n const binding: Binding | null =\n opts.bound === undefined ? null : { id: opts.bound, adopted: all.some((s) => s.id === opts.bound) };\n const graph = await analyzeDependencies(docsDir, undefined, context);\n const entries = await listFeatures(docsDir, {}, context);\n const inScope = narrowed === undefined ? entries : entries.filter((f) => f.services.some((s) => s === narrowed));\n // Once for the fleet, not once per feature: which features hold a contract\n // for which service is one fact about the repository, and every feature below\n // reads its own row out of it. Built over the UNNARROWED list on purpose — a\n // `--service` view still has to know that a feature outside it discharges the\n // contract, or the narrowing would invent an obligation.\n const owners = await contractOwners(docsDir, context);\n\n const features = await Promise.all(\n inScope.map((f) =>\n fleetFeature(docsDir, f, {\n graph,\n contracted: contractsHeldElsewhere(owners, f.id),\n living: new Map(all.map((s) => [s.id, s])),\n context,\n interrupted,\n }),\n ),\n );\n features.sort((a, b) => compareIds(a.id, b.id));\n\n // The first-hour ladder's fact, computed exactly when the ladder can apply\n // — an unnarrowed run over a fleet with zero services and zero features —\n // so the read disappears from every repository that has anything in it.\n // Withheld on narrowed runs for the same reason the binding is withheld\n // below: `--service X` is an explicit question about X, and the\n // repository's onboarding ladder would be a different question's answer.\n const teaching =\n narrowed === undefined && services.length === 0 && features.length === 0\n ? { landscape: await landscapeTeaching(docsDir) }\n : null;\n\n return {\n interrupted,\n services: {\n total: services.length,\n undocumented: services.filter(undocumented).length,\n draft: services.filter((s) => !undocumented(s) && !vouched(s)).length,\n vouched: services.filter(vouched).length,\n // A subset of `vouched`, never a fourth bucket: those services ARE\n // vouched, and the three counts still sum to `total`. It rides beside\n // the count it qualifies because this line is the fleet's trust\n // headline, and \"118 vouched\" over a fleet where 90 of them were read\n // from a sample is the misreading the scope exists to prevent.\n sampledVouched: services.filter((s) => vouched(s) && s.vouchScope === \"sampled\").length,\n // The other subset, for the same reason: \"118 vouched\" over a fleet where\n // twenty documents were edited after the person read them is the\n // trust headline lying by omission.\n contentStale: services.filter((s) => vouched(s) && s.contentStale.length > 0).length,\n },\n features,\n order: graph.order.filter((id) => features.some((f) => f.id === id)),\n service: narrowed ?? null,\n // The binding is passed only when this run was NOT narrowed, and that guard\n // is unchanged now that the binding also decides ORDER. `--service X` is an\n // explicit question about X, and answering it with a step about whichever\n // service loam.json happens to name would be a different question's answer\n // at the top of the list — which is as true of a rung promoted to the front\n // as it was of `next.adopt-bound` emitted out of nowhere. A narrowed run\n // sees at most the one service it asked about anyway, so withholding the\n // binding here costs the reader nothing and keeps `--service X` answering\n // only about X.\n next: fleetNext(\n { services, features, graph, interrupted, teaching },\n narrowed === undefined ? binding : null,\n ),\n };\n}\n\n/**\n * Which first-hour state the fleet map is in: absent, still the scaffold's own\n * untouched bytes, or authored. One file read and a byte comparison — the\n * module header's cost rule stands, no LikeC4 workspace is spun here.\n */\nasync function landscapeTeaching(docsDir: DocsDir): Promise<\"missing\" | \"stub\" | \"authored\"> {\n const lp = landscapePath(docsDir);\n if (!existsSync(lp)) return \"missing\";\n try {\n return isLandscapeStub(await readFile(lp, \"utf8\")) ? \"stub\" : \"authored\";\n } catch {\n // Unreadable is not untouched. A map that cannot be read — a directory in\n // the file's place, a permission — must not be called the scaffold's stub\n // (\"missing\" and \"stub\" would each put a false statement in next[]), so it\n // reads as \"authored\", which only drops the one teaching step: the gate\n // entry still names `loam validate --all`, where an unreadable landscape\n // is actually graded, and letting this read throw would hand the whole\n // orientation surface — a command that reports and never gates — to the\n // repository-unavailable refusal over a file status does not even parse.\n return \"authored\";\n }\n}\n\n/** What a feature's fleet row needs about the fleet AROUND it. */\ninterface FleetView {\n graph: DependencyGraph;\n /** Operations some other feature's contract already holds. */\n contracted: ReadonlySet<string>;\n /** The enumeration's entries by id — where each living service is and what it has (owesContract reads both). */\n living: ReadonlyMap<string, ServiceEntry>;\n context: FleetContext;\n interrupted: InterruptedCommit | null;\n}\n\nasync function fleetFeature(\n docsDir: DocsDir,\n feature: FeatureEntry,\n view: FleetView,\n): Promise<FleetFeatureState> {\n const { graph, contracted, living, context, interrupted } = view;\n const paths = featurePaths(feature.dir);\n const missing: string[] = [];\n if (!existsSync(paths.intent)) missing.push(\"intent\");\n if (feature.services.length === 0) missing.push(\"spec\");\n // Requirement text, not a coherence run: `Operations:` lines are read off the\n // same cached parse `list` already pays for, and the contract question needs\n // them (owesContract). No LikeC4 workspace is spun here, which is the cost\n // this form actually refuses.\n const governs = governedServices(await scanDeltas(docsDir, feature, feature.services, context));\n for (const svc of feature.services) {\n const p = featureSpecPaths(feature.dir, svc);\n // EITHER corpus satisfies it. What a service delta owes is a requirement\n // delta, and `arch.spec.md` is the other one — same grammar, same delta\n // algebra, read by `featureChecklist` — so an architecture-only feature is\n // complete and every gate already says so. This form asked for `spec.md`\n // specifically, which broke the rule the comment two lines down states\n // about the contract question: the two forms disagreed, `loam status`\n // reporting `missing <svc>/spec` over a feature `loam status <FEAT>` and\n // `loam validate --feature` both called complete. The name stays\n // `<svc>/spec` when NEITHER exists, because the business delta is the one\n // an author writes by default.\n if (!existsSync(p.spec) && !existsSync(p.archSpec)) missing.push(`${svc}/spec`);\n // The same question featureArtifacts asks, through the same function: the\n // two forms must never disagree about whether a feature owes a contract.\n if (owesContract(living.get(svc), contracted.has(svc), governs.has(svc)) && !existsSync(p.openapi)) {\n missing.push(`${svc}/openapi`);\n }\n }\n const verification = (await verificationState(docsDir, feature)).state;\n const blockedBy = graph.nodes.find((n) => n.id === feature.id)?.dependsOn ?? [];\n\n // No `draft` here, ever: that verdict needs the coherence run this form\n // refuses to pay for, and inferring it from presence alone would be a second\n // opinion about validity — the one thing status must never invent.\n //\n // The interrupted commit leads, exactly as it does in the feature form: every\n // presence test below is a question about files a killed `archive` may have\n // half-written, so answering `done` from them is a claim about bytes nobody\n // has established. Without this the two forms contradicted each other over\n // one repository — `loam status --json` said `done` and offered \"ship it\"\n // while `loam status <FEAT>` said `blocked` — and the rule in report.ts's\n // header is that this projection is never greener than the gates.\n const stage: ArtifactStatus =\n interrupted !== null\n ? \"blocked\"\n : missing.length > 0\n ? \"missing\"\n : blockedBy.length > 0\n ? \"blocked\"\n : fullyVerified(verification)\n ? \"done\"\n : \"ready\";\n\n return {\n id: feature.id,\n dirName: feature.dirName,\n path: repoPath(docsDir, feature.dir),\n stage,\n services: feature.services,\n blockedBy,\n missing,\n verification,\n };\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { serviceTreePath } from "../../kernel/ids/dirs.js";
|
|
2
2
|
import { compareIds } from "../../repo/entries.js";
|
|
3
3
|
import { recoverStep } from "../interrupted.js";
|
|
4
|
-
import { undocumented, } from "../report.js";
|
|
4
|
+
import { undocumented, vouched, } from "../report.js";
|
|
5
5
|
/** How urgent a stage is at fleet level. See {@link fleetNext}. */
|
|
6
6
|
const STAGE_RANK = {
|
|
7
7
|
done: 0,
|
|
@@ -87,8 +87,13 @@ export function fleetNext(fleet, binding) {
|
|
|
87
87
|
steps.push({
|
|
88
88
|
code: "next.bind-service",
|
|
89
89
|
statement: "No service repository is bound to this fleet yet. From a service's own repo, point --docs " +
|
|
90
|
-
"here and --service at its canonical id, then commit the
|
|
91
|
-
|
|
90
|
+
"here and --service at its canonical id with the service agent profile, then commit the " +
|
|
91
|
+
"loam.json it writes.",
|
|
92
|
+
// `--agent-profile service`, because the default is `full`, and `full`
|
|
93
|
+
// writes loam-feature and loam-ship into the service checkout — the two
|
|
94
|
+
// workflows this docs repo keeps, which README tells every service
|
|
95
|
+
// repo to leave out. An agent runs this literal, not the README.
|
|
96
|
+
command: "loam init --docs <path-to-this-docs-repo> --service <service-id> --agent-profile service",
|
|
92
97
|
});
|
|
93
98
|
steps.push({
|
|
94
99
|
code: "next.adopt-first",
|
|
@@ -124,6 +129,24 @@ export function fleetNext(fleet, binding) {
|
|
|
124
129
|
service: s.id,
|
|
125
130
|
});
|
|
126
131
|
}
|
|
132
|
+
// A vouch the document has moved out from under. `content.stale` needs
|
|
133
|
+
// nothing but the document, so unlike `sources.stale` this form CAN see it —
|
|
134
|
+
// and before it looked, a fleet whose specs were edited after the person read
|
|
135
|
+
// them got `next.fleet-clean`: "nothing is owed" over words nobody had
|
|
136
|
+
// re-read, while `loam validate` warned about each one. The re-vouch is a
|
|
137
|
+
// person's act from that service's own repository (`loam vouch` restamps the
|
|
138
|
+
// code side too), so the step names the documents and that repository, and
|
|
139
|
+
// its execution contract is a human review rather than a command to run.
|
|
140
|
+
for (const s of services.filter((x) => vouched(x) && x.contentStale.length > 0)) {
|
|
141
|
+
const files = s.contentStale.map((file) => `${serviceTreePath(s)}/${file}`);
|
|
142
|
+
perService.push({
|
|
143
|
+
code: "next.revouch",
|
|
144
|
+
statement: `${files.join(" and ")} changed since ${s.id} was vouched — \`status: verified\` stands over words nobody has re-read (content.stale). ` +
|
|
145
|
+
`A person re-reads what moved (\`loam vouch --pack --service ${s.id}\` lists it) and re-vouches from ${s.id}'s own repository.`,
|
|
146
|
+
command: `loam vouch --service ${s.id}`,
|
|
147
|
+
service: s.id,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
127
150
|
// The boundary this repository IS comes first among the boundaries it merely
|
|
128
151
|
// shares a fleet with. A stable PARTITION and deliberately not a sort: the
|
|
129
152
|
// bound service's rungs move to the front and every other service keeps the
|
|
@@ -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":"AAMA,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAqB,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EACL,YAAY,EACZ,OAAO,GAKR,MAAM,cAAc,CAAC;AAEtB,mEAAmE;AACnE,MAAM,UAAU,GAAmC;IACjD,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;IACR,KAAK,EAAE,CAAC;IACR,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,CAAC;CACX,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAqDnC,MAAM,UAAU,SAAS,CAAC,KAAY,EAAE,OAAuB;IAC7D,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IACnE,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,2EAA2E;IAC3E,6EAA6E;IAC7E,oEAAoE;IACpE,MAAM,gBAAgB,GAAG,OAAO,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAClF,MAAM,cAAc,GAAG,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAE/E,8EAA8E;IAC9E,yEAAyE;IACzE,kDAAkD;IAClD,EAAE;IACF,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,4EAA4E;IAC5E,sEAAsE;IACtE,0EAA0E;IAC1E,kEAAkE;IAClE,IAAI,gBAAgB,KAAK,IAAI,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,kBAAkB;YACxB,SAAS,EACP,2CAA2C,gBAAgB,0BAA0B;gBACrF,YAAY,gBAAgB,wEAAwE;YACtG,OAAO,EAAE,wBAAwB,gBAAgB,SAAS;YAC1D,OAAO,EAAE,gBAAgB;SAC1B,CAAC,CAAC;IACL,CAAC;IAED,2EAA2E;IAC3E,6EAA6E;IAC7E,4EAA4E;IAC5E,6EAA6E;IAC7E,6EAA6E;IAC7E,8EAA8E;IAC9E,yEAAyE;IACzE,mEAAmE;IACnE,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxE,IAAI,QAAQ,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,uBAAuB;gBAC7B,SAAS,EACP,QAAQ,CAAC,SAAS,KAAK,SAAS;oBAC9B,CAAC,CAAC,yFAAyF;wBACzF,kGAAkG;oBACpG,CAAC,CAAC,6FAA6F;wBAC7F,wEAAwE;wBACxE,oFAAoF;wBACpF,4EAA4E;gBAClF,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,mEAAmE;gBACnE,8DAA8D;gBAC9D,sEAAsE;gBACtE,oEAAoE;gBACpE,sEAAsE;gBACtE,mEAAmE;gBACnE,kDAAkD;gBAClD,OAAO,EAAE,QAAQ,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,6BAA6B;gBAChG,IAAI,EAAE,+BAA+B;aACtC,CAAC,CAAC;QACL,CAAC;QACD,IAAI,gBAAgB,KAAK,IAAI,EAAE,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,mBAAmB;gBACzB,SAAS,EACP,4FAA4F;oBAC5F,yFAAyF;oBACzF,sBAAsB;gBACxB,uEAAuE;gBACvE,wEAAwE;gBACxE,mEAAmE;gBACnE,iEAAiE;gBACjE,OAAO,EAAE,0FAA0F;aACpG,CAAC,CAAC;YACH,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,kBAAkB;gBACxB,SAAS,EACP,2FAA2F;oBAC3F,sEAAsE;gBACxE,OAAO,EAAE,0CAA0C;aACpD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,8EAA8E;IAC9E,gCAAgC;IAChC,MAAM,UAAU,GAAe,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9C,UAAU,CAAC,IAAI,CAAC;YACd,IAAI,EAAE,YAAY;YAClB,SAAS,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,oCAAoC,CAAC,CAAC,EAAE,2DAA2D;YACnI,OAAO,EAAE,wBAAwB,CAAC,CAAC,EAAE,SAAS;YAC9C,OAAO,EAAE,CAAC,CAAC,EAAE;SACd,CAAC,CAAC;IACL,CAAC;IAED,wEAAwE;IACxE,4EAA4E;IAC5E,2EAA2E;IAC3E,4DAA4D;IAC5D,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACzE,UAAU,CAAC,IAAI,CAAC;YACd,IAAI,EAAE,uBAAuB;YAC7B,SAAS,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,gHAAgH;YAChJ,OAAO,EAAE,2BAA2B,CAAC,CAAC,EAAE,SAAS;YACjD,OAAO,EAAE,CAAC,CAAC,EAAE;SACd,CAAC,CAAC;IACL,CAAC;IAED,uEAAuE;IACvE,6EAA6E;IAC7E,8EAA8E;IAC9E,uEAAuE;IACvE,0EAA0E;IAC1E,6EAA6E;IAC7E,2EAA2E;IAC3E,yEAAyE;IACzE,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;QAChF,MAAM,KAAK,GAAG,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAC5E,UAAU,CAAC,IAAI,CAAC;YACd,IAAI,EAAE,cAAc;YACpB,SAAS,EACP,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,EAAE,4FAA4F;gBACxI,+DAA+D,CAAC,CAAC,EAAE,oCAAoC,CAAC,CAAC,EAAE,oBAAoB;YACjI,OAAO,EAAE,wBAAwB,CAAC,CAAC,EAAE,EAAE;YACvC,OAAO,EAAE,CAAC,CAAC,EAAE;SACd,CAAC,CAAC;IACL,CAAC;IAED,6EAA6E;IAC7E,2EAA2E;IAC3E,4EAA4E;IAC5E,4EAA4E;IAC5E,8EAA8E;IAC9E,iBAAiB;IACjB,EAAE;IACF,6EAA6E;IAC7E,0EAA0E;IAC1E,4EAA4E;IAC5E,8EAA8E;IAC9E,4EAA4E;IAC5E,2EAA2E;IAC3E,6EAA6E;IAC7E,gCAAgC;IAChC,KAAK,CAAC,IAAI,CACR,GAAG,CAAC,cAAc,KAAK,IAAI;QACzB,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC;YACE,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,cAAc,CAAC;YACzD,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,cAAc,CAAC;SAC1D,CAAC,CACP,CAAC;IAEF,MAAM,OAAO,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAChC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC;QACzC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CACzB,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,cAAc;gBACpB,SAAS,EAAE,GAAG,CAAC,CAAC,EAAE,gFAAgF;gBAClG,OAAO,EAAE,gBAAgB,CAAC,CAAC,EAAE,mBAAmB;aACjD,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,cAAc;YACpB,SAAS;YACP,sEAAsE;YACtE,wEAAwE;YACxE,iEAAiE;YACjE,oEAAoE;YACpE,mEAAmE;YACnE,cAAc;YACd,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;gBAC7C,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBACpD,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG;YACtG,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,SAAS;SACtC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,gBAAgB,CAAC,CAAC;IAC5D,4EAA4E;IAC5E,iCAAiC;IACjC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC/G,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QACf,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,aAAa;YACnB,SAAS,EAAE,GAAG,MAAM,mIAAmI;YACvJ,OAAO,EAAE,kBAAkB;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,GAAG,CAAC,IAAI,CACN,KAAK,CAAC,MAAM,KAAK,CAAC;QAChB,CAAC,CAAC;YACE,IAAI,EAAE,kBAAkB;YACxB,SAAS,EACP,6GAA6G;YAC/G,OAAO,EAAE,4BAA4B;SACtC;QACH,CAAC,CAAC;YACE,IAAI,EAAE,iBAAiB;YACvB,2DAA2D;YAC3D,oEAAoE;YACpE,2DAA2D;YAC3D,kEAAkE;YAClE,kEAAkE;YAClE,gEAAgE;YAChE,qEAAqE;YACrE,SAAS,EACP,GAAG,QAAQ,CAAC,MAAM,8BAA8B,QAAQ,CAAC,MAAM,gBAAgB;gBAC/E,oHAAoH;YACtH,OAAO,EAAE,4BAA4B;SACtC,CACN,CAAC;IACF,OAAO,GAAG,CAAC;AACb,CAAC","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"]}
|