@ybotok/loam 0.1.0-beta.2 → 0.2.0-alpha.1
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 +2570 -61
- package/COMPARISON.md +80 -63
- package/CONTRIBUTING.md +84 -0
- package/MIGRATING-from-OpenSpec.md +213 -54
- package/QUICKSTART.md +125 -0
- package/README.md +450 -150
- package/ROADMAP.md +494 -0
- package/SCHEMA.md +2362 -245
- package/SECURITY.md +21 -3
- package/WORKFLOW.md +701 -0
- package/dist/cli.js +113 -19
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +209 -0
- package/dist/commands/adopt/adopt.js.map +1 -0
- package/dist/commands/adopt/render.js +268 -0
- package/dist/commands/adopt/render.js.map +1 -0
- package/dist/commands/archive/archive.js +77 -0
- package/dist/commands/archive/archive.js.map +1 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +159 -0
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +317 -0
- package/dist/commands/archive/plan/gate.js.map +1 -0
- package/dist/commands/archive/plan/landscape.js +161 -0
- package/dist/commands/archive/plan/landscape.js.map +1 -0
- package/dist/commands/archive/plan/refusal.js +111 -0
- package/dist/commands/archive/plan/refusal.js.map +1 -0
- package/dist/commands/archive/plan/specs.js +237 -0
- package/dist/commands/archive/plan/specs.js.map +1 -0
- package/dist/commands/archive/plan/state.js +27 -0
- package/dist/commands/archive/plan/state.js.map +1 -0
- package/dist/commands/archive/run.js +251 -0
- package/dist/commands/archive/run.js.map +1 -0
- package/dist/commands/context/context.js +99 -0
- package/dist/commands/context/context.js.map +1 -0
- package/dist/commands/context/print.js +283 -0
- package/dist/commands/context/print.js.map +1 -0
- package/dist/commands/delta/delta.js +279 -0
- package/dist/commands/delta/delta.js.map +1 -0
- package/dist/commands/delta/print.js +205 -0
- package/dist/commands/delta/print.js.map +1 -0
- package/dist/commands/dependencies.js +9 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +93 -0
- package/dist/commands/diff/diff.js.map +1 -0
- package/dist/commands/diff/print.js +38 -0
- package/dist/commands/diff/print.js.map +1 -0
- package/dist/commands/doctor.js +5 -4
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain/explain.js +151 -0
- package/dist/commands/explain/explain.js.map +1 -0
- package/dist/commands/explore.js +29 -24
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +115 -0
- package/dist/commands/gate/gate.js.map +1 -0
- package/dist/commands/gate/print.js +54 -0
- package/dist/commands/gate/print.js.map +1 -0
- package/dist/commands/gherkin/commit.js +146 -0
- package/dist/commands/gherkin/commit.js.map +1 -0
- package/dist/commands/gherkin/gherkin.js +246 -0
- package/dist/commands/gherkin/gherkin.js.map +1 -0
- package/dist/commands/gherkin/reconcile.js +149 -0
- package/dist/commands/gherkin/reconcile.js.map +1 -0
- package/dist/commands/gherkin/render.js +77 -0
- package/dist/commands/gherkin/render.js.map +1 -0
- package/dist/commands/init/example.js +204 -0
- package/dist/commands/init/example.js.map +1 -0
- package/dist/commands/init/first-hour.js +28 -0
- package/dist/commands/init/first-hour.js.map +1 -0
- package/dist/commands/{init.js → init/init.js} +146 -71
- package/dist/commands/init/init.js.map +1 -0
- package/dist/commands/init/mcp.js +131 -0
- package/dist/commands/init/mcp.js.map +1 -0
- package/dist/commands/init/options.js +78 -0
- package/dist/commands/init/options.js.map +1 -0
- package/dist/commands/instructions.js +82 -14
- package/dist/commands/instructions.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +65 -0
- package/dist/commands/list/campaign/campaign.js.map +1 -0
- package/dist/commands/list/campaign/owners.js +65 -0
- package/dist/commands/list/campaign/owners.js.map +1 -0
- package/dist/commands/list/json.js +157 -0
- package/dist/commands/list/json.js.map +1 -0
- package/dist/commands/list/list.js +343 -0
- package/dist/commands/list/list.js.map +1 -0
- package/dist/commands/list/print.js +301 -0
- package/dist/commands/list/print.js.map +1 -0
- package/dist/commands/list/review.js +120 -0
- package/dist/commands/list/review.js.map +1 -0
- package/dist/commands/list/views.js +105 -0
- package/dist/commands/list/views.js.map +1 -0
- package/dist/commands/mcp/dispatch.js +183 -0
- package/dist/commands/mcp/dispatch.js.map +1 -0
- package/dist/commands/mcp/mcp.js +34 -0
- package/dist/commands/mcp/mcp.js.map +1 -0
- package/dist/commands/mcp/serve.js +55 -0
- package/dist/commands/mcp/serve.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/active.js +170 -0
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
- package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/living.js +62 -0
- package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/target.js +219 -0
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/error.js +19 -0
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/read.js +49 -0
- package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
- package/dist/commands/migrate-openspec/print.js +89 -0
- package/dist/commands/migrate-openspec/print.js.map +1 -0
- package/dist/commands/new/commit.js +76 -0
- package/dist/commands/new/commit.js.map +1 -0
- package/dist/commands/new/new.js +283 -0
- package/dist/commands/new/new.js.map +1 -0
- package/dist/commands/new/templates.js +355 -0
- package/dist/commands/new/templates.js.map +1 -0
- package/dist/commands/open.js +203 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/policy/format.js +152 -0
- package/dist/commands/policy/format.js.map +1 -0
- package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
- package/dist/commands/policy/gate.js.map +1 -0
- package/dist/commands/rebase/edit.js +106 -0
- package/dist/commands/rebase/edit.js.map +1 -0
- package/dist/commands/rebase/living.js +185 -0
- package/dist/commands/rebase/living.js.map +1 -0
- package/dist/commands/rebase/plan.js +245 -0
- package/dist/commands/rebase/plan.js.map +1 -0
- package/dist/commands/rebase/rebase.js +275 -0
- package/dist/commands/rebase/rebase.js.map +1 -0
- package/dist/commands/seed/commit.js +192 -0
- package/dist/commands/seed/commit.js.map +1 -0
- package/dist/commands/seed/plan.js +133 -0
- package/dist/commands/seed/plan.js.map +1 -0
- package/dist/commands/seed/seed.js +172 -0
- package/dist/commands/seed/seed.js.map +1 -0
- package/dist/commands/show/feature.js +178 -0
- package/dist/commands/show/feature.js.map +1 -0
- package/dist/commands/show/marks.js +24 -0
- package/dist/commands/show/marks.js.map +1 -0
- package/dist/commands/{show.js → show/service.js} +63 -152
- package/dist/commands/show/service.js.map +1 -0
- package/dist/commands/show/show.js +77 -0
- package/dist/commands/show/show.js.map +1 -0
- package/dist/commands/status/json.js +45 -0
- package/dist/commands/status/json.js.map +1 -0
- package/dist/commands/status/print.js +157 -0
- package/dist/commands/status/print.js.map +1 -0
- package/dist/commands/status/status.js +119 -0
- package/dist/commands/status/status.js.map +1 -0
- package/dist/commands/steps/steps.js +189 -0
- package/dist/commands/steps/steps.js.map +1 -0
- package/dist/commands/subsystem/edit.js +192 -0
- package/dist/commands/subsystem/edit.js.map +1 -0
- package/dist/commands/subsystem/move.js +265 -0
- package/dist/commands/subsystem/move.js.map +1 -0
- package/dist/commands/subsystem/query.js +115 -0
- package/dist/commands/subsystem/query.js.map +1 -0
- package/dist/commands/subsystem/subsystem.js +101 -0
- package/dist/commands/subsystem/subsystem.js.map +1 -0
- package/dist/commands/subsystem/sync.js +103 -0
- package/dist/commands/subsystem/sync.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +94 -0
- package/dist/commands/subsystem/txn/txn.js.map +1 -0
- package/dist/commands/subsystem/txn/views.js +70 -0
- package/dist/commands/subsystem/txn/views.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +160 -0
- package/dist/commands/unarchive/manifest.js.map +1 -0
- package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
- package/dist/commands/unarchive/restore.js.map +1 -0
- package/dist/commands/unarchive/unarchive.js +67 -0
- package/dist/commands/unarchive/unarchive.js.map +1 -0
- package/dist/commands/validate/arch-coverage.js +194 -0
- package/dist/commands/validate/arch-coverage.js.map +1 -0
- package/dist/commands/validate/checks/fleet-shape.js +296 -0
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
- package/dist/commands/validate/checks/health-deps.js +55 -0
- package/dist/commands/validate/checks/health-deps.js.map +1 -0
- package/dist/commands/validate/checks/requirements.js +254 -0
- package/dist/commands/validate/checks/requirements.js.map +1 -0
- package/dist/commands/validate/checks/sources.js +76 -0
- package/dist/commands/validate/checks/sources.js.map +1 -0
- package/dist/commands/validate/checks/vocabulary.js +31 -0
- package/dist/commands/validate/checks/vocabulary.js.map +1 -0
- package/dist/commands/validate/feature.js +216 -0
- package/dist/commands/validate/feature.js.map +1 -0
- package/dist/commands/validate/fleet/census.js +69 -0
- package/dist/commands/validate/fleet/census.js.map +1 -0
- package/dist/commands/validate/fleet/kind-tags.js +39 -0
- package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
- package/dist/commands/validate/fleet/landscape.js +353 -0
- package/dist/commands/validate/fleet/landscape.js.map +1 -0
- package/dist/commands/validate/fleet/load.js +52 -0
- package/dist/commands/validate/fleet/load.js.map +1 -0
- package/dist/commands/validate/fleet/obligations.js +236 -0
- package/dist/commands/validate/fleet/obligations.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/print.js +63 -0
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
- package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/place.js +13 -0
- package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
- package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/steps.js +197 -0
- package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
- package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
- package/dist/commands/validate/fleet/views/ids.js +40 -0
- package/dist/commands/validate/fleet/views/ids.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +59 -0
- package/dist/commands/validate/fleet/views/stale.js.map +1 -0
- package/dist/commands/validate/links/corpus.js +92 -0
- package/dist/commands/validate/links/corpus.js.map +1 -0
- package/dist/commands/validate/links/glossary.js +75 -0
- package/dist/commands/validate/links/glossary.js.map +1 -0
- package/dist/commands/validate/report.js +269 -0
- package/dist/commands/validate/report.js.map +1 -0
- package/dist/commands/validate/service/api.js +338 -0
- package/dist/commands/validate/service/api.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +219 -0
- package/dist/commands/validate/service/events/events.js.map +1 -0
- package/dist/commands/validate/service/events/message-producers.js +77 -0
- package/dist/commands/validate/service/events/message-producers.js.map +1 -0
- package/dist/commands/validate/service/evidence-pins.js +131 -0
- package/dist/commands/validate/service/evidence-pins.js.map +1 -0
- package/dist/commands/validate/service/service.js +250 -0
- package/dist/commands/validate/service/service.js.map +1 -0
- package/dist/commands/validate/service/specs.js +219 -0
- package/dist/commands/validate/service/specs.js.map +1 -0
- package/dist/commands/validate/service/spine.js +113 -0
- package/dist/commands/validate/service/spine.js.map +1 -0
- package/dist/commands/validate/validate.js +338 -0
- package/dist/commands/validate/validate.js.map +1 -0
- package/dist/commands/verify/cross/diff.js +182 -0
- package/dist/commands/verify/cross/diff.js.map +1 -0
- package/dist/commands/verify/evidence/contract.js +32 -0
- package/dist/commands/verify/evidence/contract.js.map +1 -0
- package/dist/commands/verify/evidence/pins.js +52 -0
- package/dist/commands/verify/evidence/pins.js.map +1 -0
- package/dist/commands/verify/evidence/read.js +72 -0
- package/dist/commands/verify/evidence/read.js.map +1 -0
- package/dist/commands/verify/frozen.js +241 -0
- package/dist/commands/verify/frozen.js.map +1 -0
- package/dist/commands/verify/record.js +226 -0
- package/dist/commands/verify/record.js.map +1 -0
- package/dist/commands/verify/report.js +223 -0
- package/dist/commands/verify/report.js.map +1 -0
- package/dist/commands/verify/results.js +246 -0
- package/dist/commands/verify/results.js.map +1 -0
- package/dist/commands/verify/verify.js +217 -0
- package/dist/commands/verify/verify.js.map +1 -0
- package/dist/commands/vouch/contract.js +11 -0
- package/dist/commands/vouch/contract.js.map +1 -0
- package/dist/commands/vouch/pack/contract.js +2 -0
- package/dist/commands/vouch/pack/contract.js.map +1 -0
- package/dist/commands/vouch/pack/pack.js +266 -0
- package/dist/commands/vouch/pack/pack.js.map +1 -0
- package/dist/commands/vouch/pack/print.js +271 -0
- package/dist/commands/vouch/pack/print.js.map +1 -0
- package/dist/commands/vouch/pack/sections.js +142 -0
- package/dist/commands/vouch/pack/sections.js.map +1 -0
- package/dist/commands/vouch/prompt/confirm.js +46 -0
- package/dist/commands/vouch/prompt/confirm.js.map +1 -0
- package/dist/commands/vouch/report.js +101 -0
- package/dist/commands/vouch/report.js.map +1 -0
- package/dist/commands/vouch/run.js +285 -0
- package/dist/commands/vouch/run.js.map +1 -0
- package/dist/commands/vouch/sample/plan.js +125 -0
- package/dist/commands/vouch/sample/plan.js.map +1 -0
- package/dist/commands/vouch/sample/print.js +109 -0
- package/dist/commands/vouch/sample/print.js.map +1 -0
- package/dist/commands/vouch/vet/verify.js +123 -0
- package/dist/commands/vouch/vet/verify.js.map +1 -0
- package/dist/commands/vouch/vouch.js +176 -0
- package/dist/commands/vouch/vouch.js.map +1 -0
- package/dist/core/agent/agents-md/artifacts.js +220 -0
- package/dist/core/agent/agents-md/artifacts.js.map +1 -0
- package/dist/core/agent/agents-md/command-map.js +324 -0
- package/dist/core/agent/agents-md/command-map.js.map +1 -0
- package/dist/core/agent/agents-md/cycle.js +165 -0
- package/dist/core/agent/agents-md/cycle.js.map +1 -0
- package/dist/core/agent/agents-md/map/explain.js +34 -0
- package/dist/core/agent/agents-md/map/explain.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
- package/dist/core/agent/agents-md/map/mcp.js +41 -0
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
- package/dist/core/agent/agents-md/map/status.js +62 -0
- package/dist/core/agent/agents-md/map/status.js.map +1 -0
- package/dist/core/agent/agents-md/map/subsystem.js +32 -0
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
- package/dist/core/agent/agents-md/map/usecases.js +60 -0
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
- package/dist/core/agent/agents-md/refusals.js +246 -0
- package/dist/core/agent/agents-md/refusals.js.map +1 -0
- package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
- package/dist/core/agent/agents-md/spine.js +40 -0
- package/dist/core/agent/agents-md/spine.js.map +1 -0
- package/dist/core/agent/agents-md.js +170 -0
- package/dist/core/agent/agents-md.js.map +1 -0
- package/dist/core/agent/agents-stamp.js +155 -0
- package/dist/core/agent/agents-stamp.js.map +1 -0
- package/dist/core/agent/contract.js +2 -0
- package/dist/core/agent/contract.js.map +1 -0
- package/dist/core/agent/protocol.js +193 -0
- package/dist/core/agent/protocol.js.map +1 -0
- package/dist/core/agent/scaffold.js +259 -0
- package/dist/core/agent/scaffold.js.map +1 -0
- package/dist/core/agent/support/report.js +103 -0
- package/dist/core/agent/support/report.js.map +1 -0
- package/dist/core/agent/tools/dialects.js +150 -0
- package/dist/core/agent/tools/dialects.js.map +1 -0
- package/dist/core/agent/tools/registry.js +228 -0
- package/dist/core/agent/tools/registry.js.map +1 -0
- package/dist/core/agent/workflows/adopt.js +133 -0
- package/dist/core/agent/workflows/adopt.js.map +1 -0
- package/dist/core/agent/workflows/check.js +334 -0
- package/dist/core/agent/workflows/check.js.map +1 -0
- package/dist/core/agent/workflows/closing.js +262 -0
- package/dist/core/agent/workflows/closing.js.map +1 -0
- package/dist/core/agent/workflows/feature.js +105 -0
- package/dist/core/agent/workflows/feature.js.map +1 -0
- package/dist/core/agent/workflows/implement.js +105 -0
- package/dist/core/agent/workflows/implement.js.map +1 -0
- package/dist/core/agent/workflows/reference/authoring.js +209 -0
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
- package/dist/core/agent/workflows/reference/codes.js +65 -0
- package/dist/core/agent/workflows/reference/codes.js.map +1 -0
- package/dist/core/agent/workflows/reference/done-check.js +188 -0
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
- package/dist/core/agent/workflows/reference/reference.js +16 -0
- package/dist/core/agent/workflows/reference/reference.js.map +1 -0
- package/dist/core/agent/workflows/reference/spine.js +297 -0
- package/dist/core/agent/workflows/reference/spine.js.map +1 -0
- package/dist/core/asyncapi/baseline/plan.js +110 -0
- package/dist/core/asyncapi/baseline/plan.js.map +1 -0
- package/dist/core/asyncapi/baseline/surfaces.js +54 -0
- package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
- package/dist/core/asyncapi/depth.js +164 -0
- package/dist/core/asyncapi/depth.js.map +1 -0
- package/dist/core/asyncapi/digest.js +141 -0
- package/dist/core/asyncapi/digest.js.map +1 -0
- package/dist/core/asyncapi/merge/components.js +91 -0
- package/dist/core/asyncapi/merge/components.js.map +1 -0
- package/dist/core/asyncapi/merge/error.js +33 -0
- package/dist/core/asyncapi/merge/error.js.map +1 -0
- package/dist/core/asyncapi/merge/markers.js +308 -0
- package/dist/core/asyncapi/merge/markers.js.map +1 -0
- package/dist/core/asyncapi/merge/merge.js +215 -0
- package/dist/core/asyncapi/merge/merge.js.map +1 -0
- package/dist/core/asyncapi/merge/pin.js +107 -0
- package/dist/core/asyncapi/merge/pin.js.map +1 -0
- package/dist/core/asyncapi/model.js +11 -0
- package/dist/core/asyncapi/model.js.map +1 -0
- package/dist/core/asyncapi/producers.js +50 -0
- package/dist/core/asyncapi/producers.js.map +1 -0
- package/dist/core/asyncapi/read.js +239 -0
- package/dist/core/asyncapi/read.js.map +1 -0
- package/dist/core/brief/brief.js +225 -0
- package/dist/core/brief/brief.js.map +1 -0
- package/dist/core/brief/checks.js +250 -0
- package/dist/core/brief/checks.js.map +1 -0
- package/dist/core/brief/landscape.js +194 -0
- package/dist/core/brief/landscape.js.map +1 -0
- package/dist/core/brief/targets.js +226 -0
- package/dist/core/brief/targets.js.map +1 -0
- package/dist/core/brief/unchecked.js +47 -0
- package/dist/core/brief/unchecked.js.map +1 -0
- package/dist/core/c4/arch.js +282 -0
- package/dist/core/c4/arch.js.map +1 -0
- package/dist/core/c4/likec4.js +88 -0
- package/dist/core/c4/likec4.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js +132 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
- package/dist/core/c4/parsed/specification.js +78 -0
- package/dist/core/c4/parsed/specification.js.map +1 -0
- package/dist/core/c4/parsed/values.js +57 -0
- package/dist/core/c4/parsed/values.js.map +1 -0
- package/dist/core/c4/parsed/view-ids.js +75 -0
- package/dist/core/c4/parsed/view-ids.js.map +1 -0
- package/dist/core/c4/project/architecture.js +45 -0
- package/dist/core/c4/project/architecture.js.map +1 -0
- package/dist/core/c4/project/documents.js +58 -0
- package/dist/core/c4/project/documents.js.map +1 -0
- package/dist/core/c4/project/load.js +183 -0
- package/dist/core/c4/project/load.js.map +1 -0
- package/dist/core/c4/resolve/service.js +113 -0
- package/dist/core/c4/resolve/service.js.map +1 -0
- package/dist/core/c4/seed/fleet-file.js +201 -0
- package/dist/core/c4/seed/fleet-file.js.map +1 -0
- package/dist/core/c4/seed/items.js +91 -0
- package/dist/core/c4/seed/items.js.map +1 -0
- package/dist/core/c4/seed/stamp.js +48 -0
- package/dist/core/c4/seed/stamp.js.map +1 -0
- package/dist/core/c4/seed/template.js +109 -0
- package/dist/core/c4/seed/template.js.map +1 -0
- package/dist/core/c4/source-mask.js +82 -0
- package/dist/core/c4/source-mask.js.map +1 -0
- package/dist/core/c4/source-scan.js +227 -0
- package/dist/core/c4/source-scan.js.map +1 -0
- package/dist/core/c4/splice/authored-source.js +101 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -0
- package/dist/core/c4/splice/contract.js +10 -0
- package/dist/core/c4/splice/contract.js.map +1 -0
- package/dist/core/c4/splice/delta-blocks.js +126 -0
- package/dist/core/c4/splice/delta-blocks.js.map +1 -0
- package/dist/core/c4/splice/identity/edges.js +36 -0
- package/dist/core/c4/splice/identity/edges.js.map +1 -0
- package/dist/core/c4/splice/landscape-merge.js +269 -0
- package/dist/core/c4/splice/landscape-merge.js.map +1 -0
- package/dist/core/c4/splice/placement.js +194 -0
- package/dist/core/c4/splice/placement.js.map +1 -0
- package/dist/core/c4/workspace.js +172 -0
- package/dist/core/c4/workspace.js.map +1 -0
- package/dist/core/capabilities/authoring/notes.js +141 -0
- package/dist/core/capabilities/authoring/notes.js.map +1 -0
- package/dist/core/capabilities/capabilities.js +124 -0
- package/dist/core/capabilities/capabilities.js.map +1 -0
- package/dist/core/capabilities/delta/doc.js +50 -0
- package/dist/core/capabilities/delta/doc.js.map +1 -0
- package/dist/core/capabilities/delta/overlay.js +129 -0
- package/dist/core/capabilities/delta/overlay.js.map +1 -0
- package/dist/core/capabilities/delta/summary.js +57 -0
- package/dist/core/capabilities/delta/summary.js.map +1 -0
- package/dist/core/capabilities/delta/tree.js +43 -0
- package/dist/core/capabilities/delta/tree.js.map +1 -0
- package/dist/core/capabilities/delta/uncovered.js +222 -0
- package/dist/core/capabilities/delta/uncovered.js.map +1 -0
- package/dist/core/capabilities/findings.js +342 -0
- package/dist/core/capabilities/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/findings.js +181 -0
- package/dist/core/capabilities/realizes/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/join.js +143 -0
- package/dist/core/capabilities/realizes/join.js.map +1 -0
- package/dist/core/capabilities/rollup.js +199 -0
- package/dist/core/capabilities/rollup.js.map +1 -0
- package/dist/core/capabilities/tree.js +126 -0
- package/dist/core/capabilities/tree.js.map +1 -0
- package/dist/core/capabilities/usecase-join.js +277 -0
- package/dist/core/capabilities/usecase-join.js.map +1 -0
- package/dist/core/coherence/authoring/scaffold.js +177 -0
- package/dist/core/coherence/authoring/scaffold.js.map +1 -0
- package/dist/core/coherence/authoring/sentinels.js +74 -0
- package/dist/core/coherence/authoring/sentinels.js.map +1 -0
- package/dist/core/coherence/coherence.js +251 -0
- package/dist/core/coherence/coherence.js.map +1 -0
- package/dist/core/coherence/declared.js +269 -0
- package/dist/core/coherence/declared.js.map +1 -0
- package/dist/core/coherence/events/declared.js +206 -0
- package/dist/core/coherence/events/declared.js.map +1 -0
- package/dist/core/coherence/events/events.js +204 -0
- package/dist/core/coherence/events/events.js.map +1 -0
- package/dist/core/coherence/events/grades.js +166 -0
- package/dist/core/coherence/events/grades.js.map +1 -0
- package/dist/core/coherence/events/lookups.js +122 -0
- package/dist/core/coherence/events/lookups.js.map +1 -0
- package/dist/core/coherence/events/merged.js +40 -0
- package/dist/core/coherence/events/merged.js.map +1 -0
- package/dist/core/coherence/living.js +198 -0
- package/dist/core/coherence/living.js.map +1 -0
- package/dist/core/coherence/lookups.js +155 -0
- package/dist/core/coherence/lookups.js.map +1 -0
- package/dist/core/coherence/pending.js +39 -0
- package/dist/core/coherence/pending.js.map +1 -0
- package/dist/core/conflict-markers.js +60 -0
- package/dist/core/conflict-markers.js.map +1 -0
- package/dist/core/delta/claims.js +73 -0
- package/dist/core/delta/claims.js.map +1 -0
- package/dist/core/delta/delta.js +296 -0
- package/dist/core/delta/delta.js.map +1 -0
- package/dist/core/delta/document.js +177 -0
- package/dist/core/delta/document.js.map +1 -0
- package/dist/core/delta/scope.js +48 -0
- package/dist/core/delta/scope.js.map +1 -0
- package/dist/core/delta/select.js +237 -0
- package/dist/core/delta/select.js.map +1 -0
- package/dist/core/dependencies/dependencies.js +141 -0
- package/dist/core/dependencies/dependencies.js.map +1 -0
- package/dist/core/dependencies/facts.js +165 -0
- package/dist/core/dependencies/facts.js.map +1 -0
- package/dist/core/dependencies/fanin.js +88 -0
- package/dist/core/dependencies/fanin.js.map +1 -0
- package/dist/core/dependencies/graph.js +106 -0
- package/dist/core/dependencies/graph.js.map +1 -0
- package/dist/core/diff/base-git.js +148 -0
- package/dist/core/diff/base-git.js.map +1 -0
- package/dist/core/diff/base-state.js +209 -0
- package/dist/core/diff/base-state.js.map +1 -0
- package/dist/core/diff/current-state.js +96 -0
- package/dist/core/diff/current-state.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +188 -0
- package/dist/core/diff/scope/changed-paths.js.map +1 -0
- package/dist/core/diff/semantic.js +243 -0
- package/dist/core/diff/semantic.js.map +1 -0
- package/dist/core/diff/victims.js +247 -0
- package/dist/core/diff/victims.js.map +1 -0
- package/dist/core/docs.js +59 -55
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +222 -0
- package/dist/core/doctor/agents.js.map +1 -0
- package/dist/core/doctor/config.js +135 -0
- package/dist/core/doctor/config.js.map +1 -0
- package/dist/core/doctor/doctor.js +275 -0
- package/dist/core/doctor/doctor.js.map +1 -0
- package/dist/core/doctor/report.js +13 -0
- package/dist/core/doctor/report.js.map +1 -0
- package/dist/core/doctor/residue.js +110 -0
- package/dist/core/doctor/residue.js.map +1 -0
- package/dist/core/document/apply.js +77 -0
- package/dist/core/document/apply.js.map +1 -0
- package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
- package/dist/core/document/frontmatter.js.map +1 -0
- package/dist/core/document/grammar/directives.js +143 -0
- package/dist/core/document/grammar/directives.js.map +1 -0
- package/dist/core/document/parse.js +263 -0
- package/dist/core/document/parse.js.map +1 -0
- package/dist/core/document/scenarios.js +221 -0
- package/dist/core/document/scenarios.js.map +1 -0
- package/dist/core/document/spec.js +241 -0
- package/dist/core/document/spec.js.map +1 -0
- package/dist/core/{config.js → envelope/config.js} +79 -18
- package/dist/core/envelope/config.js.map +1 -0
- package/dist/core/envelope/json.js +175 -0
- package/dist/core/envelope/json.js.map +1 -0
- package/dist/core/{version.js → envelope/version.js} +3 -3
- package/dist/core/envelope/version.js.map +1 -0
- package/dist/core/explain/families.js +175 -0
- package/dist/core/explain/families.js.map +1 -0
- package/dist/core/explain/fix-tables.js +178 -0
- package/dist/core/explain/fix-tables.js.map +1 -0
- package/dist/core/explain/lookup.js +181 -0
- package/dist/core/explain/lookup.js.map +1 -0
- package/dist/core/explain/refusals.js +49 -0
- package/dist/core/explain/refusals.js.map +1 -0
- package/dist/core/explain/terms.js +144 -0
- package/dist/core/explain/terms.js.map +1 -0
- package/dist/core/explore/describe.js +105 -0
- package/dist/core/explore/describe.js.map +1 -0
- package/dist/core/{explore.js → explore/explore.js} +50 -101
- package/dist/core/explore/explore.js.map +1 -0
- package/dist/core/fleet-context.js +132 -77
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +201 -0
- package/dist/core/gate/checks.js.map +1 -0
- package/dist/core/gate/partners.js +205 -0
- package/dist/core/gate/partners.js.map +1 -0
- package/dist/core/gate/report.js +47 -0
- package/dist/core/gate/report.js.map +1 -0
- package/dist/core/gate/verification.js +113 -0
- package/dist/core/gate/verification.js.map +1 -0
- package/dist/core/gherkin/digest.js +60 -0
- package/dist/core/gherkin/digest.js.map +1 -0
- package/dist/core/gherkin/emit.js +177 -0
- package/dist/core/gherkin/emit.js.map +1 -0
- package/dist/core/gherkin/read.js +76 -0
- package/dist/core/gherkin/read.js.map +1 -0
- package/dist/core/gherkin/stale.js +234 -0
- package/dist/core/gherkin/stale.js.map +1 -0
- package/dist/core/gherkin/stamp.js +77 -0
- package/dist/core/gherkin/stamp.js.map +1 -0
- package/dist/core/gherkin/steps/catalogue.js +154 -0
- package/dist/core/gherkin/steps/catalogue.js.map +1 -0
- package/dist/core/gherkin/steps/inventory.js +98 -0
- package/dist/core/gherkin/steps/inventory.js.map +1 -0
- package/dist/core/gherkin/steps/phrase.js +72 -0
- package/dist/core/gherkin/steps/phrase.js.map +1 -0
- package/dist/core/glossary/delta.js +106 -0
- package/dist/core/glossary/delta.js.map +1 -0
- package/dist/core/glossary/tree.js +86 -0
- package/dist/core/glossary/tree.js.map +1 -0
- package/dist/core/kernel/concurrency.js.map +1 -0
- package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
- package/dist/core/kernel/document-bytes.js.map +1 -0
- package/dist/core/kernel/fences.js +42 -0
- package/dist/core/kernel/fences.js.map +1 -0
- package/dist/core/kernel/ids/capability.js +72 -0
- package/dist/core/kernel/ids/capability.js.map +1 -0
- package/dist/core/kernel/ids/dirs.js +33 -0
- package/dist/core/kernel/ids/dirs.js.map +1 -0
- package/dist/core/kernel/ids/feature.js +75 -0
- package/dist/core/kernel/ids/feature.js.map +1 -0
- package/dist/core/kernel/ids/service.js +156 -0
- package/dist/core/kernel/ids/service.js.map +1 -0
- package/dist/core/kernel/ids/subsystem.js +63 -0
- package/dist/core/kernel/ids/subsystem.js.map +1 -0
- package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
- package/dist/core/kernel/path-safety.js.map +1 -0
- package/dist/core/kernel/records.js.map +1 -0
- package/dist/core/links/backlinks.js +68 -0
- package/dist/core/links/backlinks.js.map +1 -0
- package/dist/core/links/case.js +74 -0
- package/dist/core/links/case.js.map +1 -0
- package/dist/core/links/corpus.js +104 -0
- package/dist/core/links/corpus.js.map +1 -0
- package/dist/core/links/findings.js +110 -0
- package/dist/core/links/findings.js.map +1 -0
- package/dist/core/links/parse.js +236 -0
- package/dist/core/links/parse.js.map +1 -0
- package/dist/core/mcp/argv.js +111 -0
- package/dist/core/mcp/argv.js.map +1 -0
- package/dist/core/mcp/author-tools.js +72 -0
- package/dist/core/mcp/author-tools.js.map +1 -0
- package/dist/core/mcp/framing.js +46 -0
- package/dist/core/mcp/framing.js.map +1 -0
- package/dist/core/mcp/protocol.js +280 -0
- package/dist/core/mcp/protocol.js.map +1 -0
- package/dist/core/mcp/tools.js +314 -0
- package/dist/core/mcp/tools.js.map +1 -0
- package/dist/core/obligations/obligations.js +106 -0
- package/dist/core/obligations/obligations.js.map +1 -0
- package/dist/core/openapi/baseline/gate.js +166 -0
- package/dist/core/openapi/baseline/gate.js.map +1 -0
- package/dist/core/openapi/baseline/plan.js +112 -0
- package/dist/core/openapi/baseline/plan.js.map +1 -0
- package/dist/core/openapi/baseline/record.js +175 -0
- package/dist/core/openapi/baseline/record.js.map +1 -0
- package/dist/core/openapi/depth.js +122 -0
- package/dist/core/openapi/depth.js.map +1 -0
- package/dist/core/openapi/digest.js +189 -0
- package/dist/core/openapi/digest.js.map +1 -0
- package/dist/core/{openapi.js → openapi/doc.js} +44 -106
- package/dist/core/openapi/doc.js.map +1 -0
- package/dist/core/openapi/merge/components.js +234 -0
- package/dist/core/openapi/merge/components.js.map +1 -0
- package/dist/core/openapi/merge/error.js +26 -0
- package/dist/core/openapi/merge/error.js.map +1 -0
- package/dist/core/openapi/merge/markers.js +208 -0
- package/dist/core/openapi/merge/markers.js.map +1 -0
- package/dist/core/openapi/merge/merge.js +256 -0
- package/dist/core/openapi/merge/merge.js.map +1 -0
- package/dist/core/openapi/merge/pin.js +128 -0
- package/dist/core/openapi/merge/pin.js.map +1 -0
- package/dist/core/openspec/decide/capabilities.js +244 -0
- package/dist/core/openspec/decide/capabilities.js.map +1 -0
- package/dist/core/openspec/decide/dispositions.js +63 -0
- package/dist/core/openspec/decide/dispositions.js.map +1 -0
- package/dist/core/openspec/decide/renames.js +179 -0
- package/dist/core/openspec/decide/renames.js.map +1 -0
- package/dist/core/openspec/decide/verdict.js +84 -0
- package/dist/core/openspec/decide/verdict.js.map +1 -0
- package/dist/core/openspec/inventory.js +221 -0
- package/dist/core/openspec/inventory.js.map +1 -0
- package/dist/core/openspec/model/mapping.js +76 -0
- package/dist/core/openspec/model/mapping.js.map +1 -0
- package/dist/core/openspec/model/model.js +65 -0
- package/dist/core/openspec/model/model.js.map +1 -0
- package/dist/core/openspec/scan/changes.js +235 -0
- package/dist/core/openspec/scan/changes.js.map +1 -0
- package/dist/core/openspec/scan/config.js +200 -0
- package/dist/core/openspec/scan/config.js.map +1 -0
- package/dist/core/openspec/scan/shape.js +260 -0
- package/dist/core/openspec/scan/shape.js.map +1 -0
- package/dist/core/openspec/scan/walk.js +133 -0
- package/dist/core/openspec/scan/walk.js.map +1 -0
- package/dist/core/openspec/scan/workspace.js +71 -0
- package/dist/core/openspec/scan/workspace.js.map +1 -0
- package/dist/core/owners/codeowners.js +141 -0
- package/dist/core/owners/codeowners.js.map +1 -0
- package/dist/core/pack/features.js +120 -0
- package/dist/core/pack/features.js.map +1 -0
- package/dist/core/pack/joins.js +112 -0
- package/dist/core/pack/joins.js.map +1 -0
- package/dist/core/pack/living.js +118 -0
- package/dist/core/pack/living.js.map +1 -0
- package/dist/core/pack/pack.js +76 -0
- package/dist/core/pack/pack.js.map +1 -0
- package/dist/core/permissions/permissions.js +87 -0
- package/dist/core/permissions/permissions.js.map +1 -0
- package/dist/core/projection/api.js +84 -0
- package/dist/core/projection/api.js.map +1 -0
- package/dist/core/projection/arch-slice.js +63 -0
- package/dist/core/projection/arch-slice.js.map +1 -0
- package/dist/core/projection/events.js +34 -0
- package/dist/core/projection/events.js.map +1 -0
- package/dist/core/provenance/findings.js +208 -0
- package/dist/core/provenance/findings.js.map +1 -0
- package/dist/core/provenance/git.js +225 -0
- package/dist/core/provenance/git.js.map +1 -0
- package/dist/core/provenance/gitq/moves.js +182 -0
- package/dist/core/provenance/gitq/moves.js.map +1 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
- package/dist/core/provenance/sample/scope.js +238 -0
- package/dist/core/provenance/sample/scope.js.map +1 -0
- package/dist/core/provenance/sample/sections.js +61 -0
- package/dist/core/provenance/sample/sections.js.map +1 -0
- package/dist/core/provenance/sources.js +263 -0
- package/dist/core/provenance/sources.js.map +1 -0
- package/dist/core/provenance/stamp.js +232 -0
- package/dist/core/provenance/stamp.js.map +1 -0
- package/dist/core/provenance/walk.js +157 -0
- package/dist/core/provenance/walk.js.map +1 -0
- package/dist/core/repo/authored/paths.js +192 -0
- package/dist/core/repo/authored/paths.js.map +1 -0
- package/dist/core/repo/entries.js +126 -0
- package/dist/core/repo/entries.js.map +1 -0
- package/dist/core/repo/paths.js +236 -0
- package/dist/core/repo/paths.js.map +1 -0
- package/dist/core/repo/repo.js +248 -0
- package/dist/core/repo/repo.js.map +1 -0
- package/dist/core/repo/service-target.js +153 -0
- package/dist/core/repo/service-target.js.map +1 -0
- package/dist/core/repo/state.js +58 -0
- package/dist/core/repo/state.js.map +1 -0
- package/dist/core/repo/tree/find.js +52 -0
- package/dist/core/repo/tree/find.js.map +1 -0
- package/dist/core/repo/tree/fs.js +64 -0
- package/dist/core/repo/tree/fs.js.map +1 -0
- package/dist/core/repo/tree/marker.js +61 -0
- package/dist/core/repo/tree/marker.js.map +1 -0
- package/dist/core/repo/tree/views.js +172 -0
- package/dist/core/repo/tree/views.js.map +1 -0
- package/dist/core/repo/tree/walk.js +234 -0
- package/dist/core/repo/tree/walk.js.map +1 -0
- package/dist/core/results.js +99 -2
- package/dist/core/results.js.map +1 -1
- package/dist/core/scaffold/landscape.js +131 -0
- package/dist/core/scaffold/landscape.js.map +1 -0
- package/dist/core/scaffold/readme.js +84 -0
- package/dist/core/scaffold/readme.js.map +1 -0
- package/dist/core/staging/commit.js +241 -0
- package/dist/core/staging/commit.js.map +1 -0
- package/dist/core/staging/interrupted.js +96 -0
- package/dist/core/staging/interrupted.js.map +1 -0
- package/dist/core/staging/lock.js +206 -0
- package/dist/core/staging/lock.js.map +1 -0
- package/dist/core/staging/recovery/finding.js +35 -0
- package/dist/core/staging/recovery/finding.js.map +1 -0
- package/dist/core/staging/recovery/intent.js +98 -0
- package/dist/core/staging/recovery/intent.js.map +1 -0
- package/dist/core/staging/recovery/recover.js +126 -0
- package/dist/core/staging/recovery/recover.js.map +1 -0
- package/dist/core/staging/recovery/residue.js +62 -0
- package/dist/core/staging/recovery/residue.js.map +1 -0
- package/dist/core/staging/snapshot.js +202 -0
- package/dist/core/staging/snapshot.js.map +1 -0
- package/dist/core/staging/txn/forward.js +171 -0
- package/dist/core/staging/txn/forward.js.map +1 -0
- package/dist/core/staging/txn/journal.js +139 -0
- package/dist/core/staging/txn/journal.js.map +1 -0
- package/dist/core/staging/txn/transaction.js +75 -0
- package/dist/core/staging/txn/transaction.js.map +1 -0
- package/dist/core/staging/writes.js +113 -0
- package/dist/core/staging/writes.js.map +1 -0
- package/dist/core/status/actions/execution.js +49 -0
- package/dist/core/status/actions/execution.js.map +1 -0
- package/dist/core/status/contracts.js +102 -0
- package/dist/core/status/contracts.js.map +1 -0
- package/dist/core/status/feature/artifacts.js +171 -0
- package/dist/core/status/feature/artifacts.js.map +1 -0
- package/dist/core/status/feature/feature.js +207 -0
- package/dist/core/status/feature/feature.js.map +1 -0
- package/dist/core/status/feature/next.js +150 -0
- package/dist/core/status/feature/next.js.map +1 -0
- package/dist/core/status/feature/state.js +21 -0
- package/dist/core/status/feature/state.js.map +1 -0
- package/dist/core/status/fleet/fleet.js +187 -0
- package/dist/core/status/fleet/fleet.js.map +1 -0
- package/dist/core/status/fleet/next.js +207 -0
- package/dist/core/status/fleet/next.js.map +1 -0
- package/dist/core/status/interrupted.js +97 -0
- package/dist/core/status/interrupted.js.map +1 -0
- package/dist/core/status/report.js +31 -0
- package/dist/core/status/report.js.map +1 -0
- package/dist/core/status/scan.js +41 -0
- package/dist/core/status/scan.js.map +1 -0
- package/dist/core/status/verification.js +201 -0
- package/dist/core/status/verification.js.map +1 -0
- package/dist/core/usecases/capability.js +169 -0
- package/dist/core/usecases/capability.js.map +1 -0
- package/dist/core/usecases/delta/claims.js +116 -0
- package/dist/core/usecases/delta/claims.js.map +1 -0
- package/dist/core/usecases/delta/flows.js +103 -0
- package/dist/core/usecases/delta/flows.js.map +1 -0
- package/dist/core/usecases/delta/overlay.js +235 -0
- package/dist/core/usecases/delta/overlay.js.map +1 -0
- package/dist/core/usecases/fleet.js +137 -0
- package/dist/core/usecases/fleet.js.map +1 -0
- package/dist/core/usecases/operations.js +111 -0
- package/dist/core/usecases/operations.js.map +1 -0
- package/dist/core/usecases/place.js +44 -0
- package/dist/core/usecases/place.js.map +1 -0
- package/dist/core/usecases/touch.js +101 -0
- package/dist/core/usecases/touch.js.map +1 -0
- package/dist/core/verify/answers.js +173 -0
- package/dist/core/verify/answers.js.map +1 -0
- package/dist/core/verify/build.js +150 -0
- package/dist/core/verify/build.js.map +1 -0
- package/dist/core/verify/checklist.js +201 -0
- package/dist/core/verify/checklist.js.map +1 -0
- package/dist/core/verify/claims/identity.js +61 -0
- package/dist/core/verify/claims/identity.js.map +1 -0
- package/dist/core/verify/cross/diff.js +97 -0
- package/dist/core/verify/cross/diff.js.map +1 -0
- package/dist/core/verify/evidence/contract.js +169 -0
- package/dist/core/verify/evidence/contract.js.map +1 -0
- package/dist/core/verify/file.js +257 -0
- package/dist/core/verify/file.js.map +1 -0
- package/dist/core/verify/pins/lint.js +157 -0
- package/dist/core/verify/pins/lint.js.map +1 -0
- package/dist/core/verify/pins/pin.js +95 -0
- package/dist/core/verify/pins/pin.js.map +1 -0
- package/dist/core/verify/record.js +142 -0
- package/dist/core/verify/record.js.map +1 -0
- package/dist/core/verify/store/commit.js +89 -0
- package/dist/core/verify/store/commit.js.map +1 -0
- package/dist/core/verify/store/render.js +57 -0
- package/dist/core/verify/store/render.js.map +1 -0
- package/dist/core/vocabulary/health.js +91 -0
- package/dist/core/vocabulary/health.js.map +1 -0
- package/dist/core/vocabulary/issue.js +55 -0
- package/dist/core/vocabulary/issue.js.map +1 -0
- package/dist/core/vocabulary/maturity.js +145 -0
- package/dist/core/vocabulary/maturity.js.map +1 -0
- package/dist/core/{report.js → vocabulary/report.js} +30 -2
- package/dist/core/vocabulary/report.js.map +1 -0
- package/dist/core/vocabulary/steps.js +266 -0
- package/dist/core/vocabulary/steps.js.map +1 -0
- package/dist/core/workspace/discover.js +239 -0
- package/dist/core/workspace/discover.js.map +1 -0
- package/dist/core/workspace/render.js +52 -0
- package/dist/core/workspace/render.js.map +1 -0
- package/examples/README.md +189 -0
- package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
- package/examples/docs/architecture/capabilities.yaml +31 -0
- package/examples/docs/architecture/landscape.likec4 +280 -0
- package/examples/docs/architecture/obligations.yaml +23 -0
- package/examples/docs/architecture/permissions.yaml +50 -0
- package/examples/docs/architecture/subsystems.likec4 +11 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
- package/examples/docs/capabilities/checkout/spec.md +55 -0
- package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
- package/examples/docs/capabilities/order-notifications/spec.md +43 -0
- package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
- package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
- package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
- package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
- package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
- package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
- package/examples/docs/glossary/order.md +15 -0
- package/examples/docs/glossary/payments/authorization.md +14 -0
- package/examples/docs/likec4.config.json +9 -0
- package/examples/docs/loam.json +3 -0
- package/examples/docs/services/checkout-web/model.likec4 +109 -0
- package/examples/docs/services/checkout-web/spec.md +91 -0
- package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
- package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
- package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
- package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
- package/examples/docs/services/order-service/arch.spec.md +127 -0
- package/examples/docs/services/order-service/asyncapi.yaml +108 -0
- package/examples/docs/services/order-service/health.yaml +57 -0
- package/examples/docs/services/order-service/model.likec4 +138 -0
- package/examples/docs/services/order-service/openapi.yaml +257 -0
- package/examples/docs/services/order-service/runbook.md +62 -0
- package/examples/docs/services/order-service/spec.md +134 -0
- package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
- package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
- package/examples/docs/services/payment-service/arch.spec.md +73 -0
- package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
- package/examples/docs/services/payment-service/health.yaml +39 -0
- package/examples/docs/services/payment-service/model.likec4 +67 -0
- package/examples/docs/services/payment-service/openapi.yaml +151 -0
- package/examples/docs/services/payment-service/runbook.md +37 -0
- package/examples/docs/services/payment-service/spec.md +88 -0
- package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
- package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
- package/examples/docs/services/platform/identity-service/health.yaml +70 -0
- package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
- package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
- package/examples/docs/services/platform/identity-service/runbook.md +95 -0
- package/examples/docs/services/platform/identity-service/spec.md +119 -0
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
- package/examples/docs/services/platform/notification-service/health.yaml +35 -0
- package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
- package/examples/docs/services/platform/notification-service/runbook.md +48 -0
- package/examples/docs/services/platform/notification-service/spec.md +112 -0
- package/examples/docs/services/platform/subsystem.yaml +2 -0
- package/examples/fleet.yaml +57 -0
- package/package.json +19 -6
- package/dist/commands/adopt.js +0 -240
- package/dist/commands/adopt.js.map +0 -1
- package/dist/commands/archive.js +0 -1313
- package/dist/commands/archive.js.map +0 -1
- package/dist/commands/delta.js +0 -388
- package/dist/commands/delta.js.map +0 -1
- package/dist/commands/docs-repo-gate.js.map +0 -1
- package/dist/commands/format.js +0 -17
- package/dist/commands/format.js.map +0 -1
- package/dist/commands/gherkin.js +0 -309
- package/dist/commands/gherkin.js.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/list.js +0 -291
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/migrate-openspec.js +0 -950
- package/dist/commands/migrate-openspec.js.map +0 -1
- package/dist/commands/new.js +0 -413
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/rebase.js +0 -356
- package/dist/commands/rebase.js.map +0 -1
- package/dist/commands/show.js.map +0 -1
- package/dist/commands/status.js +0 -254
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/unarchive.js.map +0 -1
- package/dist/commands/validate.js +0 -1591
- package/dist/commands/validate.js.map +0 -1
- package/dist/commands/verify.js +0 -775
- package/dist/commands/verify.js.map +0 -1
- package/dist/commands/vouch.js +0 -343
- package/dist/commands/vouch.js.map +0 -1
- package/dist/core/agent.js +0 -2065
- package/dist/core/agent.js.map +0 -1
- package/dist/core/agents-stamp.js +0 -68
- package/dist/core/agents-stamp.js.map +0 -1
- package/dist/core/arch.js +0 -130
- package/dist/core/arch.js.map +0 -1
- package/dist/core/brief.js +0 -733
- package/dist/core/brief.js.map +0 -1
- package/dist/core/coherence.js +0 -577
- package/dist/core/coherence.js.map +0 -1
- package/dist/core/concurrency.js.map +0 -1
- package/dist/core/config.js.map +0 -1
- package/dist/core/delta.js +0 -476
- package/dist/core/delta.js.map +0 -1
- package/dist/core/dependencies.js +0 -356
- package/dist/core/dependencies.js.map +0 -1
- package/dist/core/doctor.js +0 -559
- package/dist/core/doctor.js.map +0 -1
- package/dist/core/document-bytes.js.map +0 -1
- package/dist/core/explore.js.map +0 -1
- package/dist/core/frontmatter.js.map +0 -1
- package/dist/core/gherkin.js +0 -463
- package/dist/core/gherkin.js.map +0 -1
- package/dist/core/health.js +0 -67
- package/dist/core/health.js.map +0 -1
- package/dist/core/ids.js +0 -122
- package/dist/core/ids.js.map +0 -1
- package/dist/core/issue.js +0 -9
- package/dist/core/issue.js.map +0 -1
- package/dist/core/json.js +0 -74
- package/dist/core/json.js.map +0 -1
- package/dist/core/likec4.js +0 -479
- package/dist/core/likec4.js.map +0 -1
- package/dist/core/maturity.js +0 -66
- package/dist/core/maturity.js.map +0 -1
- package/dist/core/openapi-merge.js +0 -552
- package/dist/core/openapi-merge.js.map +0 -1
- package/dist/core/openapi.js.map +0 -1
- package/dist/core/openspec-inventory.js +0 -1440
- package/dist/core/openspec-inventory.js.map +0 -1
- package/dist/core/path-safety.js.map +0 -1
- package/dist/core/provenance.js +0 -720
- package/dist/core/provenance.js.map +0 -1
- package/dist/core/records.js.map +0 -1
- package/dist/core/repo.js +0 -436
- package/dist/core/repo.js.map +0 -1
- package/dist/core/report.js.map +0 -1
- package/dist/core/spec.js +0 -481
- package/dist/core/spec.js.map +0 -1
- package/dist/core/staging.js +0 -890
- package/dist/core/staging.js.map +0 -1
- package/dist/core/status.js +0 -1104
- package/dist/core/status.js.map +0 -1
- package/dist/core/steps.js +0 -72
- package/dist/core/steps.js.map +0 -1
- package/dist/core/verify.js +0 -807
- package/dist/core/verify.js.map +0 -1
- package/dist/core/version.js.map +0 -1
- /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
- /package/dist/core/{records.js → kernel/records.js} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inventory.js","sourceRoot":"","sources":["../../../../src/core/gherkin/steps/inventory.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The step-phrase inventory: how many DEFINITIONS a service's suite actually\n * needs, and which written steps collapse onto each one.\n *\n * ## What this is for\n *\n * Generated `.feature` files are only a suite if somebody writes the glue, and\n * the glue is where a generated suite dies: the step text is free prose from a\n * spec bullet, so the same precondition gets three spellings across three\n * requirements and the registry grows without bound until people quietly go\n * back to hand-written tests. Nothing in loam could previously say how bad that\n * was, which meant nobody could tell a suite that needs forty definitions from\n * one that needs four hundred.\n *\n * Measured on a real twelve-scenario component-test file, 324 written steps\n * collapse to 97 phrases, and 34 of those cover 82% of every step — so the\n * report is not an audit, it is the work-list a team writes glue from, and the\n * draft of any step catalogue that comes later.\n *\n * ## What it deliberately is NOT\n *\n * It emits no finding and no stable code. A near-duplicate is a judgement about\n * writing, and a phrase-similarity WARNING is the kind of check a fleet turns\n * off — at which point the real findings beside it are turned off too. It reads\n * the LIVING specs rather than the emitted files, so it answers the same way in\n * a repo that has never run `loam gherkin` and needs no service repo to stand\n * in.\n */\nimport { scenarioGherkin } from \"../../vocabulary/steps.js\";\nimport { type Requirement } from \"../../document/spec.js\";\nimport { keywordOf, phraseOf } from \"./phrase.js\";\n\n/** Where one written step lives, for the reader who wants to go and look. */\nexport interface PhraseUse {\n requirement: string;\n scenario: string;\n /** \"business\" or \"arch\" — the two spec files are two namespaces everywhere else too. */\n axis: string;\n /** The step exactly as the spec writes it, keyword included. */\n text: string;\n}\n\nexport interface PhraseRow {\n /** The normalized phrase — one step definition serves every use under it. */\n key: string;\n /** How many written steps collapse onto this key. */\n count: number;\n /** The Gherkin keywords this phrase appears under, sorted — a runner resolves them as one. */\n keywords: string[];\n uses: PhraseUse[];\n}\n\n/** Phrases that share a family and not a key: two definitions where one was probably meant. */\nexport interface NearDuplicates {\n family: string;\n keys: string[];\n}\n\nexport interface StepInventory {\n /** Total written steps across every scenario of both axes. */\n steps: number;\n /** Distinct phrases, most-used first, then alphabetically so the report is stable. */\n phrases: PhraseRow[];\n nearDuplicates: NearDuplicates[];\n}\n\n/** One spec file's requirements, with the axis label the whole product speaks. */\nexport interface InventoryAxis {\n axis: string;\n reqs: Requirement[];\n}\n\n/**\n * Count the phrases across both axes of one service.\n *\n * REMOVED requirements are skipped, exactly as every other scenario check skips\n * them: a step nobody will run is not glue anybody owes.\n *\n * Ordering is count-descending then key-ascending, and that second key is what\n * makes the report diffable — two phrases used once each must not swap places\n * between runs because a Map happened to iterate differently.\n */\nexport function stepInventory(axes: readonly InventoryAxis[]): StepInventory {\n const byKey = new Map<string, PhraseRow>();\n const families = new Map<string, Set<string>>();\n let steps = 0;\n for (const { axis, reqs } of axes) {\n for (const r of reqs) {\n if (r.kind === \"REMOVED\") continue;\n for (const s of r.scenarios) {\n for (const st of scenarioGherkin(s.lines).steps) {\n steps += 1;\n const { key, family } = phraseOf(st.text);\n const row = byKey.get(key) ?? { key, count: 0, keywords: [], uses: [] };\n row.count += 1;\n const kw = keywordOf(st.text);\n if (kw.length > 0 && !row.keywords.includes(kw)) row.keywords.push(kw);\n row.uses.push({ requirement: r.name, scenario: s.name, axis, text: st.text });\n byKey.set(key, row);\n const seen = families.get(family) ?? new Set<string>();\n seen.add(key);\n families.set(family, seen);\n }\n }\n }\n }\n const phrases = [...byKey.values()]\n .map((row) => ({ ...row, keywords: [...row.keywords].sort() }))\n .sort((a, b) => b.count - a.count || (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));\n const nearDuplicates = [...families.entries()]\n .filter(([, keys]) => keys.size > 1)\n .map(([family, keys]) => ({ family, keys: [...keys].sort() }))\n .sort((a, b) => (a.family < b.family ? -1 : a.family > b.family ? 1 : 0));\n return { steps, phrases, nearDuplicates };\n}\n\n/**\n * How many phrases cover `share` of every written step — the one number that\n * says whether the glue is a morning's work or a quarter's.\n *\n * Returns zero for an empty suite rather than dividing by it, because \"0 of 0\n * steps\" is the honest reading of a service that has written no scenarios yet.\n */\nexport function coveringPhrases(inv: StepInventory, share = 0.8): number {\n if (inv.steps === 0) return 0;\n const target = inv.steps * share;\n let running = 0;\n let n = 0;\n for (const row of inv.phrases) {\n if (running >= target) break;\n running += row.count;\n n += 1;\n }\n return n;\n}\n"]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The PHRASE KEY: what makes two written steps the same step definition.
|
|
3
|
+
*
|
|
4
|
+
* `loam steps` counts by this key, so the moment it ships the recipe is a
|
|
5
|
+
* contract — a team writes glue against the groups it reports, and a key that
|
|
6
|
+
* drifts re-partitions their whole registry. `test/steps-inventory.test.ts`
|
|
7
|
+
* pins it case by case for that reason.
|
|
8
|
+
*
|
|
9
|
+
* ## Why the keyword is dropped
|
|
10
|
+
*
|
|
11
|
+
* Every mainstream runner resolves Given/When/Then/And/But against ONE registry
|
|
12
|
+
* of patterns, so `Given the ledger is open` and `And the ledger is open` are
|
|
13
|
+
* one definition and must count as one row. Keeping the keyword would split
|
|
14
|
+
* every continued step from the step it continues, and the number the whole
|
|
15
|
+
* report exists to give — how many definitions does this suite need — would be
|
|
16
|
+
* wrong in the direction that flatters it.
|
|
17
|
+
*
|
|
18
|
+
* ## Why literals collapse and prose does not
|
|
19
|
+
*
|
|
20
|
+
* A quoted string, an angle-bracket placeholder and a bare number are what a
|
|
21
|
+
* step definition captures as an ARGUMENT; everything else is what it matches
|
|
22
|
+
* on. Collapsing exactly those three is therefore not a heuristic about
|
|
23
|
+
* similarity — it is the same partition the runner will make when the patterns
|
|
24
|
+
* are written. Nothing here measures edit distance: `core/repo/entries.ts`
|
|
25
|
+
* already records that verdict for close-id suggestions, and a third private
|
|
26
|
+
* copy of that function would be the drift it warns about.
|
|
27
|
+
*/
|
|
28
|
+
const KEYWORD_RE = /^(given|when|then|and|but)\s+/i;
|
|
29
|
+
/**
|
|
30
|
+
* A subordinate clause that explains WHY, which a step definition never
|
|
31
|
+
* matches on separately: `validation branch "x" passes because the window is
|
|
32
|
+
* open` is the same definition as `validation branch "x" passes`, with the
|
|
33
|
+
* reason baked into the pattern. Reported as a near-duplicate rather than
|
|
34
|
+
* merged, because the fix — move the reason into the scenario description — is
|
|
35
|
+
* the author's to make.
|
|
36
|
+
*/
|
|
37
|
+
const RATIONALE_RE = /\s+(because|while|since|unless|so that)\s+.*$/i;
|
|
38
|
+
const LEADING_ARTICLE_RE = /^(the|a|an)\s+/i;
|
|
39
|
+
/** `Given a payment of 100.00 for "<tier>"` -> key `a payment of {n} for {s}`. */
|
|
40
|
+
export function phraseOf(stepText) {
|
|
41
|
+
const key = stepText
|
|
42
|
+
.replace(KEYWORD_RE, "")
|
|
43
|
+
// A QUOTED placeholder is still a placeholder — `"<tier>"` is the outline's
|
|
44
|
+
// parameter, and letting the string rule reach it first would collapse the
|
|
45
|
+
// whole token to {s} and hide that the step is parameterized at all.
|
|
46
|
+
.replace(/(["'`])<[^<>\s]+>\1/g, "{p}")
|
|
47
|
+
.replace(/<[^<>\s]+>/g, "{p}")
|
|
48
|
+
.replace(/"[^"]*"/g, "{s}")
|
|
49
|
+
.replace(/'[^']*'/g, "{s}")
|
|
50
|
+
.replace(/`[^`]*`/g, "{s}")
|
|
51
|
+
// A number only when it stands alone: `v1` and `oauth2` are part of a name,
|
|
52
|
+
// and collapsing them would merge two steps that match different patterns.
|
|
53
|
+
.replace(/(?<![\w.])\d+(?:\.\d+)?(?![\w.])/g, "{n}")
|
|
54
|
+
.replace(/\s+/g, " ")
|
|
55
|
+
.trim();
|
|
56
|
+
const family = key
|
|
57
|
+
.replace(RATIONALE_RE, "")
|
|
58
|
+
.replace(LEADING_ARTICLE_RE, "")
|
|
59
|
+
.replace(/[.,;:]+$/, "")
|
|
60
|
+
.replace(/\s+/g, " ")
|
|
61
|
+
.trim();
|
|
62
|
+
return { key, family };
|
|
63
|
+
}
|
|
64
|
+
/** The canonical keyword of a written step line, for reporting which ones a phrase appears under. */
|
|
65
|
+
export function keywordOf(stepText) {
|
|
66
|
+
const m = KEYWORD_RE.exec(stepText);
|
|
67
|
+
if (!m)
|
|
68
|
+
return "";
|
|
69
|
+
const w = m[1].toLowerCase();
|
|
70
|
+
return w[0].toUpperCase() + w.slice(1);
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=phrase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phrase.js","sourceRoot":"","sources":["../../../../src/core/gherkin/steps/phrase.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,UAAU,GAAG,gCAAgC,CAAC;AAEpD;;;;;;;GAOG;AACH,MAAM,YAAY,GAAG,gDAAgD,CAAC;AAEtE,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAa7C,kFAAkF;AAClF,MAAM,UAAU,QAAQ,CAAC,QAAgB;IACvC,MAAM,GAAG,GAAG,QAAQ;SACjB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;QACxB,4EAA4E;QAC5E,2EAA2E;QAC3E,qEAAqE;SACpE,OAAO,CAAC,sBAAsB,EAAE,KAAK,CAAC;SACtC,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC;SAC7B,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC;SAC1B,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC;SAC1B,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC;QAC3B,4EAA4E;QAC5E,2EAA2E;SAC1E,OAAO,CAAC,mCAAmC,EAAE,KAAK,CAAC;SACnD,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;IACV,MAAM,MAAM,GAAG,GAAG;SACf,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;SACzB,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;SAC/B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;IACV,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;AACzB,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC;IAC9B,OAAO,CAAC,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC1C,CAAC","sourcesContent":["/**\n * The PHRASE KEY: what makes two written steps the same step definition.\n *\n * `loam steps` counts by this key, so the moment it ships the recipe is a\n * contract — a team writes glue against the groups it reports, and a key that\n * drifts re-partitions their whole registry. `test/steps-inventory.test.ts`\n * pins it case by case for that reason.\n *\n * ## Why the keyword is dropped\n *\n * Every mainstream runner resolves Given/When/Then/And/But against ONE registry\n * of patterns, so `Given the ledger is open` and `And the ledger is open` are\n * one definition and must count as one row. Keeping the keyword would split\n * every continued step from the step it continues, and the number the whole\n * report exists to give — how many definitions does this suite need — would be\n * wrong in the direction that flatters it.\n *\n * ## Why literals collapse and prose does not\n *\n * A quoted string, an angle-bracket placeholder and a bare number are what a\n * step definition captures as an ARGUMENT; everything else is what it matches\n * on. Collapsing exactly those three is therefore not a heuristic about\n * similarity — it is the same partition the runner will make when the patterns\n * are written. Nothing here measures edit distance: `core/repo/entries.ts`\n * already records that verdict for close-id suggestions, and a third private\n * copy of that function would be the drift it warns about.\n */\n\nconst KEYWORD_RE = /^(given|when|then|and|but)\\s+/i;\n\n/**\n * A subordinate clause that explains WHY, which a step definition never\n * matches on separately: `validation branch \"x\" passes because the window is\n * open` is the same definition as `validation branch \"x\" passes`, with the\n * reason baked into the pattern. Reported as a near-duplicate rather than\n * merged, because the fix — move the reason into the scenario description — is\n * the author's to make.\n */\nconst RATIONALE_RE = /\\s+(because|while|since|unless|so that)\\s+.*$/i;\n\nconst LEADING_ARTICLE_RE = /^(the|a|an)\\s+/i;\n\nexport interface Phrase {\n /** Two steps sharing this need ONE step definition. */\n key: string;\n /**\n * The looser key that gathers near-duplicates: the same phrase with a leading\n * article or a trailing rationale clause. Steps sharing a family but not a key\n * are two definitions where the author probably meant one.\n */\n family: string;\n}\n\n/** `Given a payment of 100.00 for \"<tier>\"` -> key `a payment of {n} for {s}`. */\nexport function phraseOf(stepText: string): Phrase {\n const key = stepText\n .replace(KEYWORD_RE, \"\")\n // A QUOTED placeholder is still a placeholder — `\"<tier>\"` is the outline's\n // parameter, and letting the string rule reach it first would collapse the\n // whole token to {s} and hide that the step is parameterized at all.\n .replace(/([\"'`])<[^<>\\s]+>\\1/g, \"{p}\")\n .replace(/<[^<>\\s]+>/g, \"{p}\")\n .replace(/\"[^\"]*\"/g, \"{s}\")\n .replace(/'[^']*'/g, \"{s}\")\n .replace(/`[^`]*`/g, \"{s}\")\n // A number only when it stands alone: `v1` and `oauth2` are part of a name,\n // and collapsing them would merge two steps that match different patterns.\n .replace(/(?<![\\w.])\\d+(?:\\.\\d+)?(?![\\w.])/g, \"{n}\")\n .replace(/\\s+/g, \" \")\n .trim();\n const family = key\n .replace(RATIONALE_RE, \"\")\n .replace(LEADING_ARTICLE_RE, \"\")\n .replace(/[.,;:]+$/, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n return { key, family };\n}\n\n/** The canonical keyword of a written step line, for reporting which ones a phrase appears under. */\nexport function keywordOf(stepText: string): string {\n const m = KEYWORD_RE.exec(stepText);\n if (!m) return \"\";\n const w = m[1]!.toLowerCase();\n return w[0]!.toUpperCase() + w.slice(1);\n}\n"]}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A feature's own glossary deltas: `features/<FEAT>/glossary/<term>.md`.
|
|
3
|
+
*
|
|
4
|
+
* A change that needs a new word brings the word with it. The archive copies the
|
|
5
|
+
* definition into `glossary/`, `loam unarchive` takes it back out with
|
|
6
|
+
* everything else the feature landed, and until then the term is visible in
|
|
7
|
+
* review beside the requirements that use it — which is the whole reason this
|
|
8
|
+
* route exists rather than "remember to edit the glossary too".
|
|
9
|
+
*
|
|
10
|
+
* CREATE-ONLY, and this is the axis's one real design decision rather than an
|
|
11
|
+
* unfinished half. A feature-local capability document is a DELTA — `## ADDED
|
|
12
|
+
* Requirements`, `Requirement-ID:` identity, `Based-On:` pins — because it
|
|
13
|
+
* merges INTO a living document and two features editing one requirement would
|
|
14
|
+
* otherwise silently overwrite each other. A term has none of that: the file is
|
|
15
|
+
* one definition, the merge is a whole-file copy, and there is nothing to merge
|
|
16
|
+
* partially. So rewriting a living definition through a feature would be a
|
|
17
|
+
* silent whole-file replacement with no pin to collide on, where editing
|
|
18
|
+
* `glossary/<term>.md` directly in the same pull request produces an ordinary
|
|
19
|
+
* git conflict that a human resolves. `glossary.term-exists` refuses the first
|
|
20
|
+
* and points at the second.
|
|
21
|
+
*
|
|
22
|
+
* TWO FEATURES INTRODUCING THE SAME WORD is the case that makes the refusal
|
|
23
|
+
* load-bearing rather than pedantic: whichever archives first creates the living
|
|
24
|
+
* definition, and the second is told — loudly, before it merges — that the word
|
|
25
|
+
* now exists and its own file is the one to delete.
|
|
26
|
+
*
|
|
27
|
+
* ONE CALL for the walk, because it is already written: `readGlossary`
|
|
28
|
+
* implements the file-is-a-term rule, the `README.md` exclusion, the nesting and
|
|
29
|
+
* the symlink guard over any root. A second walk for the feature side would be
|
|
30
|
+
* two implementations free to disagree about what a term is.
|
|
31
|
+
*/
|
|
32
|
+
import { existsSync } from "node:fs";
|
|
33
|
+
import { join, resolve } from "node:path";
|
|
34
|
+
import { featureGlossaryDir, glossaryDir } from "../repo/authored/paths.js";
|
|
35
|
+
import {} from "../vocabulary/issue.js";
|
|
36
|
+
import { readGlossary } from "./tree.js";
|
|
37
|
+
/**
|
|
38
|
+
* The definitions one feature carries, ordered by id.
|
|
39
|
+
*
|
|
40
|
+
* `present: false` — and zero readdirs — when the feature has no `glossary/`
|
|
41
|
+
* directory, which is every feature in a fleet that has not adopted the axis.
|
|
42
|
+
* That short-circuit is `readGlossary`'s own `existsSync` and it is the whole
|
|
43
|
+
* cost such a fleet pays.
|
|
44
|
+
*/
|
|
45
|
+
export function featureGlossary(featureDir) {
|
|
46
|
+
return readGlossary(featureGlossaryDir(featureDir));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Where each of this feature's terms WILL live once the archive merges — the
|
|
50
|
+
* overlay a feature's own documents are allowed to cite.
|
|
51
|
+
*
|
|
52
|
+
* A feature that introduces a word cites it at its future living path
|
|
53
|
+
* (`../../glossary/order.md`), because that is where the link has to point once
|
|
54
|
+
* the feature ships; citing the delta path instead would resolve today and rot
|
|
55
|
+
* on the day it lands. So while a feature is in flight, `link.unresolved` treats
|
|
56
|
+
* these paths as resolved — the same overlay the capability axis needed for
|
|
57
|
+
* `Realizes:`, and for the same reason: an index built from the LIVING tree
|
|
58
|
+
* alone refuses the feature's own headline flow.
|
|
59
|
+
*/
|
|
60
|
+
export async function featureGlossaryOverlay(docsDir, featureDir) {
|
|
61
|
+
const glossary = await featureGlossary(featureDir);
|
|
62
|
+
return new Set(glossary.terms.map((t) => resolve(livingTermPath(docsDir, t.id))));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* `glossary.term-exists` — a term this feature introduces that the living
|
|
66
|
+
* glossary already defines.
|
|
67
|
+
*
|
|
68
|
+
* An ERROR and never a warning-that-gates: unlike `capability.uncovered`, there
|
|
69
|
+
* is no legal reading of it. The merge would replace an authored definition
|
|
70
|
+
* with another one, and no `--approve` makes that a different act than it is.
|
|
71
|
+
* The fix is in the message and is the one a reviewer would give anyway.
|
|
72
|
+
*/
|
|
73
|
+
export async function glossaryDeltaIssues(docsDir, featureDir) {
|
|
74
|
+
const glossary = await featureGlossary(featureDir);
|
|
75
|
+
if (!glossary.present)
|
|
76
|
+
return [];
|
|
77
|
+
return glossary.terms
|
|
78
|
+
.filter((term) => existsSync(livingTermPath(docsDir, term.id)))
|
|
79
|
+
.map((term) => ({
|
|
80
|
+
severity: "error",
|
|
81
|
+
code: "glossary.term-exists",
|
|
82
|
+
subject: term.id,
|
|
83
|
+
message: `glossary/${term.id}.md already exists, so this feature's definition of '${term.id}' would replace an authored one wholesale — ` +
|
|
84
|
+
"a feature-local glossary document INTRODUCES a term, it does not rewrite one. " +
|
|
85
|
+
`Delete features/<FEAT>/glossary/${term.id}.md and edit glossary/${term.id}.md directly in this same change, ` +
|
|
86
|
+
"where git produces an ordinary conflict if somebody else is editing it too.",
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The LIVING path a feature-local term id addresses. Spelled here because three
|
|
91
|
+
* readers need it — the refusal above, the overlay, and the archive merge — and
|
|
92
|
+
* a reader that resolved it differently would refuse a term the merge then
|
|
93
|
+
* created anyway.
|
|
94
|
+
*
|
|
95
|
+
* The id is joined un-split, which `node:path` normalizes: `join(dir, "a/b")`
|
|
96
|
+
* and `join(dir, "a", "b")` are the same string. What holds this join is
|
|
97
|
+
* PROVENANCE, exactly as it holds `livingCapabilityPaths` — every id here came
|
|
98
|
+
* out of a `readGlossary` walk, where each component is a `readdir` entry name
|
|
99
|
+
* and therefore a directory or file that exists. No caller takes one from argv;
|
|
100
|
+
* the day one does, it needs the treatment `core/kernel/ids/capability.ts` gave
|
|
101
|
+
* the same hazard one axis over.
|
|
102
|
+
*/
|
|
103
|
+
export function livingTermPath(docsDir, id) {
|
|
104
|
+
return join(glossaryDir(docsDir), `${id}.md`);
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=delta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delta.js","sourceRoot":"","sources":["../../../src/core/glossary/delta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC5E,OAAO,EAAc,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAiB,MAAM,WAAW,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,UAAsB;IACpD,OAAO,YAAY,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,OAAgB,EAAE,UAAsB;IACnF,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,UAAU,CAAC,CAAC;IACnD,OAAO,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACpF,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAAgB,EAAE,UAAsB;IAChF,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,UAAU,CAAC,CAAC;IACnD,IAAI,CAAC,QAAQ,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACjC,OAAO,QAAQ,CAAC,KAAK;SAClB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;SAC9D,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACd,QAAQ,EAAE,OAAgB;QAC1B,IAAI,EAAE,sBAA+B;QACrC,OAAO,EAAE,IAAI,CAAC,EAAE;QAChB,OAAO,EACL,YAAY,IAAI,CAAC,EAAE,wDAAwD,IAAI,CAAC,EAAE,8CAA8C;YAChI,gFAAgF;YAChF,mCAAmC,IAAI,CAAC,EAAE,yBAAyB,IAAI,CAAC,EAAE,oCAAoC;YAC9G,6EAA6E;KAChF,CAAC,CAAC,CAAC;AACR,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgB,EAAE,EAAU;IACzD,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAChD,CAAC","sourcesContent":["/**\n * A feature's own glossary deltas: `features/<FEAT>/glossary/<term>.md`.\n *\n * A change that needs a new word brings the word with it. The archive copies the\n * definition into `glossary/`, `loam unarchive` takes it back out with\n * everything else the feature landed, and until then the term is visible in\n * review beside the requirements that use it — which is the whole reason this\n * route exists rather than \"remember to edit the glossary too\".\n *\n * CREATE-ONLY, and this is the axis's one real design decision rather than an\n * unfinished half. A feature-local capability document is a DELTA — `## ADDED\n * Requirements`, `Requirement-ID:` identity, `Based-On:` pins — because it\n * merges INTO a living document and two features editing one requirement would\n * otherwise silently overwrite each other. A term has none of that: the file is\n * one definition, the merge is a whole-file copy, and there is nothing to merge\n * partially. So rewriting a living definition through a feature would be a\n * silent whole-file replacement with no pin to collide on, where editing\n * `glossary/<term>.md` directly in the same pull request produces an ordinary\n * git conflict that a human resolves. `glossary.term-exists` refuses the first\n * and points at the second.\n *\n * TWO FEATURES INTRODUCING THE SAME WORD is the case that makes the refusal\n * load-bearing rather than pedantic: whichever archives first creates the living\n * definition, and the second is told — loudly, before it merges — that the word\n * now exists and its own file is the one to delete.\n *\n * ONE CALL for the walk, because it is already written: `readGlossary`\n * implements the file-is-a-term rule, the `README.md` exclusion, the nesting and\n * the symlink guard over any root. A second walk for the feature side would be\n * two implementations free to disagree about what a term is.\n */\nimport { existsSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { featureGlossaryDir, glossaryDir } from \"../repo/authored/paths.js\";\nimport { type Issue } from \"../vocabulary/issue.js\";\nimport type { DocsDir, FeatureDir } from \"../kernel/ids/dirs.js\";\nimport { readGlossary, type Glossary } from \"./tree.js\";\n\n/**\n * The definitions one feature carries, ordered by id.\n *\n * `present: false` — and zero readdirs — when the feature has no `glossary/`\n * directory, which is every feature in a fleet that has not adopted the axis.\n * That short-circuit is `readGlossary`'s own `existsSync` and it is the whole\n * cost such a fleet pays.\n */\nexport function featureGlossary(featureDir: FeatureDir): Promise<Glossary> {\n return readGlossary(featureGlossaryDir(featureDir));\n}\n\n/**\n * Where each of this feature's terms WILL live once the archive merges — the\n * overlay a feature's own documents are allowed to cite.\n *\n * A feature that introduces a word cites it at its future living path\n * (`../../glossary/order.md`), because that is where the link has to point once\n * the feature ships; citing the delta path instead would resolve today and rot\n * on the day it lands. So while a feature is in flight, `link.unresolved` treats\n * these paths as resolved — the same overlay the capability axis needed for\n * `Realizes:`, and for the same reason: an index built from the LIVING tree\n * alone refuses the feature's own headline flow.\n */\nexport async function featureGlossaryOverlay(docsDir: DocsDir, featureDir: FeatureDir): Promise<Set<string>> {\n const glossary = await featureGlossary(featureDir);\n return new Set(glossary.terms.map((t) => resolve(livingTermPath(docsDir, t.id))));\n}\n\n/**\n * `glossary.term-exists` — a term this feature introduces that the living\n * glossary already defines.\n *\n * An ERROR and never a warning-that-gates: unlike `capability.uncovered`, there\n * is no legal reading of it. The merge would replace an authored definition\n * with another one, and no `--approve` makes that a different act than it is.\n * The fix is in the message and is the one a reviewer would give anyway.\n */\nexport async function glossaryDeltaIssues(docsDir: DocsDir, featureDir: FeatureDir): Promise<Issue[]> {\n const glossary = await featureGlossary(featureDir);\n if (!glossary.present) return [];\n return glossary.terms\n .filter((term) => existsSync(livingTermPath(docsDir, term.id)))\n .map((term) => ({\n severity: \"error\" as const,\n code: \"glossary.term-exists\" as const,\n subject: term.id,\n message:\n `glossary/${term.id}.md already exists, so this feature's definition of '${term.id}' would replace an authored one wholesale — ` +\n \"a feature-local glossary document INTRODUCES a term, it does not rewrite one. \" +\n `Delete features/<FEAT>/glossary/${term.id}.md and edit glossary/${term.id}.md directly in this same change, ` +\n \"where git produces an ordinary conflict if somebody else is editing it too.\",\n }));\n}\n\n/**\n * The LIVING path a feature-local term id addresses. Spelled here because three\n * readers need it — the refusal above, the overlay, and the archive merge — and\n * a reader that resolved it differently would refuse a term the merge then\n * created anyway.\n *\n * The id is joined un-split, which `node:path` normalizes: `join(dir, \"a/b\")`\n * and `join(dir, \"a\", \"b\")` are the same string. What holds this join is\n * PROVENANCE, exactly as it holds `livingCapabilityPaths` — every id here came\n * out of a `readGlossary` walk, where each component is a `readdir` entry name\n * and therefore a directory or file that exists. No caller takes one from argv;\n * the day one does, it needs the treatment `core/kernel/ids/capability.ts` gave\n * the same hazard one axis over.\n */\nexport function livingTermPath(docsDir: DocsDir, id: string): string {\n return join(glossaryDir(docsDir), `${id}.md`);\n}\n"]}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The glossary tree: `glossary/<term>.md`, and the rule that a markdown file
|
|
3
|
+
* under it IS a declared term.
|
|
4
|
+
*
|
|
5
|
+
* WHY A TREE AND NOT A YAML VOCABULARY, at length in `repo/authored/paths.ts`'s
|
|
6
|
+
* `glossaryDir`: a single vocabulary file at fleet scale is unworkable, and a
|
|
7
|
+
* second list is the drift `loam init`'s removed service manifest was removed
|
|
8
|
+
* for. A term is prose — that is what a definition is — so by this codebase's
|
|
9
|
+
* own rule it gets a file.
|
|
10
|
+
*
|
|
11
|
+
* PRESENCE CLASSIFIES, one tree over from `capabilities/`: a `.md` file is a
|
|
12
|
+
* term, a directory is a group the walk descends through. There is no
|
|
13
|
+
* "half-created term" state to name, which is why this walk emits nothing like
|
|
14
|
+
* `capability.doc-missing`: creating a term is writing one file, and an empty
|
|
15
|
+
* grouping directory declares nothing and claims nothing.
|
|
16
|
+
*
|
|
17
|
+
* `README.md` IS NOT A TERM, and it is the one name excluded. A glossary big
|
|
18
|
+
* enough to want an index page is normal, and reporting that page as a word
|
|
19
|
+
* nobody uses would make the axis's only warning fire on the document that
|
|
20
|
+
* explains the axis. Excluded by name rather than by a marker field: the
|
|
21
|
+
* alternative is frontmatter nobody would remember to write.
|
|
22
|
+
*
|
|
23
|
+
* The walk reads directories only and never opens a file. What is IN a term
|
|
24
|
+
* document is prose for a human, and the one thing loam reads from it — the
|
|
25
|
+
* links it writes — is read by `core/links/`, where every other document's are.
|
|
26
|
+
*/
|
|
27
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
28
|
+
import { readdir } from "node:fs/promises";
|
|
29
|
+
import { join } from "node:path";
|
|
30
|
+
import { compareIds } from "../repo/entries.js";
|
|
31
|
+
import { entryIs } from "../repo/tree/fs.js";
|
|
32
|
+
/** The one filename under `glossary/` that is not a term. */
|
|
33
|
+
const INDEX_PAGE = "README.md";
|
|
34
|
+
/**
|
|
35
|
+
* Read the tree under `root` (`glossaryDir(docsDir)`).
|
|
36
|
+
*
|
|
37
|
+
* A fleet is expected to hold tens to low hundreds of these — a domain's
|
|
38
|
+
* vocabulary, not its data — so the cost is one `readdir` per directory and the
|
|
39
|
+
* walk's expense is a non-question. The symlink-cycle guard is the one
|
|
40
|
+
* `core/repo/tree/walk.ts` and the capability walk both carry, and for the same
|
|
41
|
+
* reason: `entryIs` follows symlinks on purpose, and a followed cycle without a
|
|
42
|
+
* visited set recurses until the stack ends.
|
|
43
|
+
*/
|
|
44
|
+
export async function readGlossary(root) {
|
|
45
|
+
if (!existsSync(root))
|
|
46
|
+
return { present: false, terms: [] };
|
|
47
|
+
const state = { terms: [], visited: new Set() };
|
|
48
|
+
await visit({ dir: root, chain: [] }, state);
|
|
49
|
+
state.terms.sort((a, b) => compareIds(a.id, b.id));
|
|
50
|
+
return { present: true, terms: state.terms };
|
|
51
|
+
}
|
|
52
|
+
/** Collect the terms in one directory and walk what is beneath it. */
|
|
53
|
+
async function visit(spot, state) {
|
|
54
|
+
if (!descended(spot.dir, state))
|
|
55
|
+
return;
|
|
56
|
+
const entries = (await readdir(spot.dir, { withFileTypes: true })).filter((e) => !e.name.startsWith("."));
|
|
57
|
+
for (const entry of entries.filter((e) => e.name.endsWith(".md")).sort((a, b) => compareIds(a.name, b.name))) {
|
|
58
|
+
if (entry.name === INDEX_PAGE || !entryIs(spot.dir, entry, "file"))
|
|
59
|
+
continue;
|
|
60
|
+
state.terms.push({
|
|
61
|
+
id: [...spot.chain, entry.name.slice(0, -".md".length)].join("/"),
|
|
62
|
+
path: join(spot.dir, entry.name),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// Sequential, not Promise.all: the visited set is shared mutable state and
|
|
66
|
+
// the walk's correctness under symlinks rests on a child being marked before
|
|
67
|
+
// its sibling asks. These trees are tens of directories at most.
|
|
68
|
+
for (const child of entries.filter((e) => entryIs(spot.dir, e, "dir")).map((e) => e.name).sort()) {
|
|
69
|
+
await visit({ dir: join(spot.dir, child), chain: [...spot.chain, child] }, state);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Mark a directory as descended-into; false when it (or another name for it) already was. */
|
|
73
|
+
function descended(dir, state) {
|
|
74
|
+
let real;
|
|
75
|
+
try {
|
|
76
|
+
real = realpathSync(dir);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
if (state.visited.has(real))
|
|
82
|
+
return false;
|
|
83
|
+
state.visited.add(real);
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.js","sourceRoot":"","sources":["../../../src/core/glossary/tree.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The glossary tree: `glossary/<term>.md`, and the rule that a markdown file\n * under it IS a declared term.\n *\n * WHY A TREE AND NOT A YAML VOCABULARY, at length in `repo/authored/paths.ts`'s\n * `glossaryDir`: a single vocabulary file at fleet scale is unworkable, and a\n * second list is the drift `loam init`'s removed service manifest was removed\n * for. A term is prose — that is what a definition is — so by this codebase's\n * own rule it gets a file.\n *\n * PRESENCE CLASSIFIES, one tree over from `capabilities/`: a `.md` file is a\n * term, a directory is a group the walk descends through. There is no\n * \"half-created term\" state to name, which is why this walk emits nothing like\n * `capability.doc-missing`: creating a term is writing one file, and an empty\n * grouping directory declares nothing and claims nothing.\n *\n * `README.md` IS NOT A TERM, and it is the one name excluded. A glossary big\n * enough to want an index page is normal, and reporting that page as a word\n * nobody uses would make the axis's only warning fire on the document that\n * explains the axis. Excluded by name rather than by a marker field: the\n * alternative is frontmatter nobody would remember to write.\n *\n * The walk reads directories only and never opens a file. What is IN a term\n * document is prose for a human, and the one thing loam reads from it — the\n * links it writes — is read by `core/links/`, where every other document's are.\n */\nimport { existsSync, realpathSync } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { compareIds } from \"../repo/entries.js\";\nimport { entryIs } from \"../repo/tree/fs.js\";\n\n/** The one filename under `glossary/` that is not a term. */\nconst INDEX_PAGE = \"README.md\";\n\n/** One defined term: what it is called, and where its definition is. */\nexport interface GlossaryTerm {\n /** The path from `glossary/` to the file, without `.md` — `order`, or `payments/order`. */\n id: string;\n /** Absolute path of the definition. */\n path: string;\n}\n\nexport interface Glossary {\n /**\n * False when `glossary/` does not exist. The directory's existence is the\n * axis's opt-in, so a fleet without one produces nothing here — not an empty\n * glossary, which would read as \"the terms went missing\" to anything counting.\n */\n present: boolean;\n /** Every term found, at any depth, ordered by id. */\n terms: GlossaryTerm[];\n}\n\n/** One directory the walk is looking at: where it is, and how it was reached. */\ninterface Spot {\n dir: string;\n /** Directory names between `glossary/` and this one, outermost first. */\n chain: string[];\n}\n\ninterface WalkState {\n terms: GlossaryTerm[];\n /** Realpaths already descended into — the symlink-cycle guard. */\n visited: Set<string>;\n}\n\n/**\n * Read the tree under `root` (`glossaryDir(docsDir)`).\n *\n * A fleet is expected to hold tens to low hundreds of these — a domain's\n * vocabulary, not its data — so the cost is one `readdir` per directory and the\n * walk's expense is a non-question. The symlink-cycle guard is the one\n * `core/repo/tree/walk.ts` and the capability walk both carry, and for the same\n * reason: `entryIs` follows symlinks on purpose, and a followed cycle without a\n * visited set recurses until the stack ends.\n */\nexport async function readGlossary(root: string): Promise<Glossary> {\n if (!existsSync(root)) return { present: false, terms: [] };\n const state: WalkState = { terms: [], visited: new Set() };\n await visit({ dir: root, chain: [] }, state);\n state.terms.sort((a, b) => compareIds(a.id, b.id));\n return { present: true, terms: state.terms };\n}\n\n/** Collect the terms in one directory and walk what is beneath it. */\nasync function visit(spot: Spot, state: WalkState): Promise<void> {\n if (!descended(spot.dir, state)) return;\n const entries = (await readdir(spot.dir, { withFileTypes: true })).filter((e) => !e.name.startsWith(\".\"));\n for (const entry of entries.filter((e) => e.name.endsWith(\".md\")).sort((a, b) => compareIds(a.name, b.name))) {\n if (entry.name === INDEX_PAGE || !entryIs(spot.dir, entry, \"file\")) continue;\n state.terms.push({\n id: [...spot.chain, entry.name.slice(0, -\".md\".length)].join(\"/\"),\n path: join(spot.dir, entry.name),\n });\n }\n // Sequential, not Promise.all: the visited set is shared mutable state and\n // the walk's correctness under symlinks rests on a child being marked before\n // its sibling asks. These trees are tens of directories at most.\n for (const child of entries.filter((e) => entryIs(spot.dir, e, \"dir\")).map((e) => e.name).sort()) {\n await visit({ dir: join(spot.dir, child), chain: [...spot.chain, child] }, state);\n }\n}\n\n/** Mark a directory as descended-into; false when it (or another name for it) already was. */\nfunction descended(dir: string, state: WalkState): boolean {\n let real: string;\n try {\n real = realpathSync(dir);\n } catch {\n return false;\n }\n if (state.visited.has(real)) return false;\n state.visited.add(real);\n return true;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.js","sourceRoot":"","sources":["../../../src/core/kernel/concurrency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;AAE1E;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,KAAmB,EACnB,IAA6B;IAE7B,MAAM,GAAG,GAAQ,EAAE,CAAC;IACpB,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3D,MAAM,OAAO,CAAC,GAAG,CACf,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,KAAK,IAAI,EAAE;QACzC,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;IACpF,CAAC,CAAC,CACH,CAAC;IACF,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["/**\n * How much of loam may happen at once, and in what order the answers come back.\n *\n * One rule, because there is one resource being rationed: a LikeC4/Langium\n * workspace. `validate --all` opens one per service and per feature, the\n * dependency graph opens one per feature, and an unbounded `Promise.all` over\n * either opens all of them — on a big fleet that is not a faster run, it is a\n * bigger one. The cap and the ordering guarantee travel together because a\n * caller that wants the first cannot afford to lose the second.\n */\nimport { cpus } from \"node:os\";\n\n/**\n * How many units of work may be in flight at once.\n *\n * Capped as well as scaled: each in-flight unit holds a whole LikeC4/Langium\n * workspace, so a 64-core box would open sixty-four of them at once and buy\n * memory pressure rather than throughput (peak RSS on an 80-service fleet:\n * 385 MB serial, ~500 MB pooled). That measurement is the entire justification\n * for the cap, and it is why the cap lives with the work rather than inside one\n * command: `loam dependencies` and the fleet form of `loam status` fan out over\n * the same workspaces `validate --all` does, and used to do it uncapped.\n *\n * Do not quote a speedup for this. The measured cost is CPU inside the Langium\n * parse, which one thread cannot divide however the loop is shaped; what the\n * pool overlaps is the I/O half — the spec, contract and health reads around\n * each parse. On an 80-service fleet it measures as a wash (11–13 s either way,\n * on a box that was itself loaded). The number that moves these commands is\n * workspaces per run, not awaits per loop.\n */\nexport const TARGET_CONCURRENCY = Math.max(1, Math.min(cpus().length, 8));\n\n/**\n * Run `work` over `items` with a bounded number in flight, returning the\n * results in INPUT order.\n *\n * The order is the contract, not an accident: the text report and the `--json`\n * envelope are diffed between runs and between machines, so a concurrent run\n * has to be byte-identical to the serial one for the same input. That is why\n * results are written by index — nothing is appended as it finishes, and a slow\n * item cannot reorder the fleet around it.\n */\nexport async function inOrder<T, R>(\n items: readonly T[],\n work: (item: T) => Promise<R>,\n): Promise<R[]> {\n const out: R[] = [];\n let next = 0;\n const workers = Math.min(TARGET_CONCURRENCY, items.length);\n await Promise.all(\n Array.from({ length: workers }, async () => {\n for (let i = next++; i < items.length; i = next++) out[i] = await work(items[i]!);\n }),\n );\n return out;\n}\n"]}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* the frontmatter reader, the requirement parser, the fleet-wide read index and
|
|
6
6
|
* three commands that read a single file directly. It lived beside the read
|
|
7
7
|
* index, so each of those imported the whole index (and through it repo.ts,
|
|
8
|
-
* openapi.ts and spec.ts) to reach a dozen lines of decoding, and several of
|
|
8
|
+
* openapi/doc.ts and document/spec.ts) to reach a dozen lines of decoding, and several of
|
|
9
9
|
* them closed an import cycle doing it. A leaf whose only dependency is
|
|
10
10
|
* `node:buffer` costs nothing to import and can be reached from anywhere.
|
|
11
11
|
*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-bytes.js","sourceRoot":"","sources":["../../../src/core/kernel/document-bytes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAIlC,IAAI;IAHG,IAAI,GAAG,sBAAsB,CAAC;IAEhD,YACW,IAAY,EACrB,MAAc;QAEd,KAAK,CACH,GAAG,IAAI,mCAAmC,MAAM,6DAA6D;YAC3G,+GAA+G;YAC/G,8GAA8G;YAC9G,2DAA2D,CAC9D,CAAC;oBARO,IAAI;IASf,CAAC;CACF;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,IAAY;IACxD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,oBAAoB,CAAC,IAAI,EAAE,0CAA0C,CAAC,CAAC;IACrG,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,oBAAoB,CAC5B,IAAI,EACJ,iHAAiH,CAClH,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["/**\n * Decode the bytes of a document loam is about to parse, or refuse naming it.\n *\n * The rule belongs to every module that turns a markdown artifact into text —\n * the frontmatter reader, the requirement parser, the fleet-wide read index and\n * three commands that read a single file directly. It lived beside the read\n * index, so each of those imported the whole index (and through it repo.ts,\n * openapi/doc.ts and document/spec.ts) to reach a dozen lines of decoding, and several of\n * them closed an import cycle doing it. A leaf whose only dependency is\n * `node:buffer` costs nothing to import and can be reached from anywhere.\n */\nimport { isUtf8 } from \"node:buffer\";\n\n/**\n * A document loam was about to parse that is not UTF-8 text.\n *\n * Thrown rather than returned, and named as the file rather than as an empty\n * document, because every parser in the codebase reads \"no requirements, no\n * frontmatter\" as a legitimate state — a fresh baseline looks exactly like it.\n * A `spec.md` saved as UTF-16 (PowerShell's `>` writes UTF-16LE by default)\n * therefore validated green, with every requirement in it invisible.\n *\n * `path` is spelled the way Node spells it on an ErrnoException so that\n * validate's per-target guard names the file without knowing this type; the\n * write path makes the same two tests for the same reasons (staging.ts's\n * `decodeUtf8`), and its messages differ only in saying what a rewrite would\n * additionally destroy, which is not what a reader needs to hear.\n */\nexport class NotUtf8DocumentError extends Error {\n override readonly name = \"NotUtf8DocumentError\";\n\n constructor(\n readonly path: string,\n reason: string,\n ) {\n super(\n `${path} is not a UTF-8 text document — ${reason}. Read as UTF-8 its requirements, frontmatter and headings ` +\n `all come out absent rather than wrong, so the file grades as an empty baseline instead of an unreadable one. ` +\n `Nothing here was checked. Re-save it as UTF-8 (PowerShell's \\`>\\` and \\`Out-File\\` write UTF-16 unless told ` +\n `otherwise: use \\`Out-File -Encoding utf8\\`), then re-run.`,\n );\n }\n}\n\n/**\n * Decode a document loam is about to parse, or refuse naming it.\n *\n * Two tests, because one is not enough. `isUtf8` catches the byte sequences\n * that cannot be UTF-8 at all — which is what a byte-order mark makes UTF-16\n * into, and PowerShell writes one. Without a BOM, UTF-16LE of ASCII is a\n * sequence of valid UTF-8 bytes: every other byte is NUL, which decodes to\n * U+0000 and nothing complains. That file parsed as zero requirements and no\n * frontmatter, and validated green. A NUL in a markdown document loam owns has\n * no legitimate spelling, so it is the second test.\n */\nexport function decodeDocument(bytes: Buffer, path: string): string {\n if (!isUtf8(bytes)) throw new NotUtf8DocumentError(path, \"its bytes are not a valid UTF-8 sequence\");\n const text = bytes.toString(\"utf8\");\n if (text.includes(\"\\u0000\")) {\n throw new NotUtf8DocumentError(\n path,\n \"it holds NUL characters, which no markdown document does — almost always UTF-16 saved without a byte-order mark\",\n );\n }\n return text;\n}\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Track ``` / ~~~ fences line by line — the one rule every markdown walk in the
|
|
3
|
+
* product has to agree on about what is structure and what is quoted text.
|
|
4
|
+
*
|
|
5
|
+
* It lived in `core/document/parse.ts` while that file was its only writer and
|
|
6
|
+
* `core/provenance/sample/sections.ts` its only borrower. It moved here when the
|
|
7
|
+
* fourth reader arrived and could not reach it: `core/vocabulary/steps.ts` needs
|
|
8
|
+
* the same rule to stop a pipe table inside a fenced payload from being hoisted
|
|
9
|
+
* out as a scenario's `Examples`, and `core/document/` already imports
|
|
10
|
+
* `core/vocabulary/` (through `document/scenarios.ts`), so an import the other
|
|
11
|
+
* way would close a package cycle the graph check forbids. AGENTS.md names this
|
|
12
|
+
* move explicitly: when a helper you need lives in a module heavier than the
|
|
13
|
+
* helper, move the helper out rather than importing the weight.
|
|
14
|
+
*
|
|
15
|
+
* `core/kernel/` is where it lands because it is the one package below every
|
|
16
|
+
* other — it imports nothing from `core/` at all, so nobody can create a cycle
|
|
17
|
+
* by depending on it.
|
|
18
|
+
*
|
|
19
|
+
* Returns true while the line is fenced content — INCLUDING the fence marker
|
|
20
|
+
* itself. Callers that need the markers back (an emitter reading a docstring out
|
|
21
|
+
* of a body, rather than a heading walk skipping over one) must track the
|
|
22
|
+
* transition themselves; this predicate deliberately answers one question.
|
|
23
|
+
*
|
|
24
|
+
* A closing marker must match the opener's character, so a ``` block quoting a
|
|
25
|
+
* ~~~ line stays open, which is what the markdown spec says and what stops a
|
|
26
|
+
* payload containing fence characters from ending its own block early.
|
|
27
|
+
*/
|
|
28
|
+
export function fenceTracker() {
|
|
29
|
+
let fence = null;
|
|
30
|
+
return (line) => {
|
|
31
|
+
const m = /^\s*(```|~~~)/.exec(line);
|
|
32
|
+
if (m) {
|
|
33
|
+
if (fence === null)
|
|
34
|
+
fence = m[1];
|
|
35
|
+
else if (fence === m[1])
|
|
36
|
+
fence = null;
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
return fence !== null;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=fences.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fences.js","sourceRoot":"","sources":["../../../src/core/kernel/fences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,YAAY;IAC1B,IAAI,KAAK,GAAkB,IAAI,CAAC;IAChC,OAAO,CAAC,IAAI,EAAE,EAAE;QACd,MAAM,CAAC,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,EAAE,CAAC;YACN,IAAI,KAAK,KAAK,IAAI;gBAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;iBAC7B,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAE;gBAAE,KAAK,GAAG,IAAI,CAAC;YACvC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,KAAK,IAAI,CAAC;IACxB,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Track ``` / ~~~ fences line by line — the one rule every markdown walk in the\n * product has to agree on about what is structure and what is quoted text.\n *\n * It lived in `core/document/parse.ts` while that file was its only writer and\n * `core/provenance/sample/sections.ts` its only borrower. It moved here when the\n * fourth reader arrived and could not reach it: `core/vocabulary/steps.ts` needs\n * the same rule to stop a pipe table inside a fenced payload from being hoisted\n * out as a scenario's `Examples`, and `core/document/` already imports\n * `core/vocabulary/` (through `document/scenarios.ts`), so an import the other\n * way would close a package cycle the graph check forbids. AGENTS.md names this\n * move explicitly: when a helper you need lives in a module heavier than the\n * helper, move the helper out rather than importing the weight.\n *\n * `core/kernel/` is where it lands because it is the one package below every\n * other — it imports nothing from `core/` at all, so nobody can create a cycle\n * by depending on it.\n *\n * Returns true while the line is fenced content — INCLUDING the fence marker\n * itself. Callers that need the markers back (an emitter reading a docstring out\n * of a body, rather than a heading walk skipping over one) must track the\n * transition themselves; this predicate deliberately answers one question.\n *\n * A closing marker must match the opener's character, so a ``` block quoting a\n * ~~~ line stays open, which is what the markdown spec says and what stops a\n * payload containing fence characters from ending its own block early.\n */\nexport function fenceTracker(): (line: string) => boolean {\n let fence: string | null = null;\n return (line) => {\n const m = /^\\s*(```|~~~)/.exec(line);\n if (m) {\n if (fence === null) fence = m[1]!;\n else if (fence === m[1]!) fence = null;\n return true;\n }\n return fence !== null;\n };\n}\n"]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The spelling rule for a capability id that ARRIVED FROM ARGV — the one
|
|
3
|
+
* provenance `repo/authored/paths.ts` says must never reach a path join unchecked.
|
|
4
|
+
*
|
|
5
|
+
* `livingCapabilityPaths` takes a plain `string`, and its comment states why: a
|
|
6
|
+
* capability id has no grammar of its own — it is a YAML key and a chain of
|
|
7
|
+
* directory names — so what holds that join is PROVENANCE. Every id it sees
|
|
8
|
+
* today came out of a `readCapabilityTree` walk, where each component is a
|
|
9
|
+
* `readdir` entry name and therefore demonstrably a directory that exists.
|
|
10
|
+
* (`featureCapabilityDeltasDir` beside it takes a branded `FeatureDir` and no
|
|
11
|
+
* capability id at all; the feature-side join is spelled by `loam new` itself.)
|
|
12
|
+
*
|
|
13
|
+
* `loam new --capability <id>` is the first caller with no such provenance. The
|
|
14
|
+
* string is whatever a person typed, and it is joined as
|
|
15
|
+
* `features/<dirName>/capabilities/<id>/spec.md` — so `--capability ../../evil`
|
|
16
|
+
* resolves to `features/evil/spec.md`, a directory `listFeatures` then
|
|
17
|
+
* enumerates as a feature, and one `..` further reaches the docs-repo root.
|
|
18
|
+
* `resolveInside` cannot refuse either: both are still inside the repo. That is
|
|
19
|
+
* the failure this module closes, and it is the same one
|
|
20
|
+
* `core/kernel/ids/service.ts` opens with one file over.
|
|
21
|
+
*
|
|
22
|
+
* IT IS THE DIRECTORY-NAME RULE, NOT A NEW ONE. Nesting is spelled by the tree
|
|
23
|
+
* (`payments/refunds` is two directories), so an id is legal exactly when every
|
|
24
|
+
* one of its `/`-separated segments is a legal directory name in the docs repo
|
|
25
|
+
* — `dirNameHazard`, the same check a service id and a subsystem name are held
|
|
26
|
+
* to, Windows device names and trailing dots included. A capability tree that
|
|
27
|
+
* only clones onto some of the machines that read it is the same defect there
|
|
28
|
+
* as it is under `services/`.
|
|
29
|
+
*
|
|
30
|
+
* NO BRAND, deliberately, and the reason is the provenance rule above rather
|
|
31
|
+
* than an omission: a brand is worth its annotations when a path builder can
|
|
32
|
+
* DEMAND it, and neither builder can — both are called with walk-derived ids
|
|
33
|
+
* that were never parsed here and must keep working. What this module gives is
|
|
34
|
+
* the check the argv path owes; the rule that walk-derived ids are safe stays
|
|
35
|
+
* where `repo/paths.ts` already writes it down.
|
|
36
|
+
*/
|
|
37
|
+
import { dirNameHazard } from "./service.js";
|
|
38
|
+
/** Prose form of the rule, shown verbatim in the refusal so the fix is obvious. */
|
|
39
|
+
const CAPABILITY_ID_RULE = "a capability id is one or more '/'-separated segments, each starting with a letter or digit and " +
|
|
40
|
+
"containing only letters, digits, '.', '_' or '-' (no '..', no spaces, no leading or trailing '/') — " +
|
|
41
|
+
"it becomes the capabilities/<id>/ directory chain, one directory per segment";
|
|
42
|
+
/**
|
|
43
|
+
* Why `id` cannot be a capability id, or null when it can.
|
|
44
|
+
*
|
|
45
|
+
* A sentence rather than a boolean, matching `serviceIdProblem`: a caller that
|
|
46
|
+
* is collecting findings prints exactly what a refusal would.
|
|
47
|
+
*/
|
|
48
|
+
export function capabilityIdProblem(id, label = "--capability") {
|
|
49
|
+
if (typeof id !== "string" || id === "") {
|
|
50
|
+
return `${label} must name a capability: ${CAPABILITY_ID_RULE}.`;
|
|
51
|
+
}
|
|
52
|
+
for (const segment of id.split("/")) {
|
|
53
|
+
const hazard = dirNameHazard(segment);
|
|
54
|
+
if (hazard === null)
|
|
55
|
+
continue;
|
|
56
|
+
switch (hazard.kind) {
|
|
57
|
+
case "grammar":
|
|
58
|
+
return `Invalid ${label} '${id}': ${CAPABILITY_ID_RULE}.`;
|
|
59
|
+
// Both Windows refusals keep their own sentence for `serviceIdProblem`'s
|
|
60
|
+
// reason: "does not match the grammar" is true and useless, because the
|
|
61
|
+
// id LOOKS fine and the problem is what the filesystem does with it.
|
|
62
|
+
case "windows-device":
|
|
63
|
+
return (`Invalid ${label} '${id}': '${hazard.stem}' is a reserved device name on Windows — ` +
|
|
64
|
+
`the capabilities/${id}/ directory cannot be created there at all, so the docs repo would only work on some of the machines that clone it.`);
|
|
65
|
+
case "windows-trailing":
|
|
66
|
+
return (`Invalid ${label} '${id}': no segment of a capability id may end with '.' or a space — ` +
|
|
67
|
+
"Windows strips both when creating a directory, so the id would silently name one directory there and another everywhere else.");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=capability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability.js","sourceRoot":"","sources":["../../../../src/core/kernel/ids/capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,mFAAmF;AACnF,MAAM,kBAAkB,GACtB,kGAAkG;IAClG,sGAAsG;IACtG,8EAA8E,CAAC;AAEjF;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAW,EAAE,KAAK,GAAG,cAAc;IACrE,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACxC,OAAO,GAAG,KAAK,4BAA4B,kBAAkB,GAAG,CAAC;IACnE,CAAC;IACD,KAAK,MAAM,OAAO,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,MAAM,KAAK,IAAI;YAAE,SAAS;QAC9B,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,SAAS;gBACZ,OAAO,WAAW,KAAK,KAAK,EAAE,MAAM,kBAAkB,GAAG,CAAC;YAC5D,yEAAyE;YACzE,wEAAwE;YACxE,qEAAqE;YACrE,KAAK,gBAAgB;gBACnB,OAAO,CACL,WAAW,KAAK,KAAK,EAAE,OAAO,MAAM,CAAC,IAAI,2CAA2C;oBACpF,oBAAoB,EAAE,qHAAqH,CAC5I,CAAC;YACJ,KAAK,kBAAkB;gBACrB,OAAO,CACL,WAAW,KAAK,KAAK,EAAE,iEAAiE;oBACxF,+HAA+H,CAChI,CAAC;QACN,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["/**\n * The spelling rule for a capability id that ARRIVED FROM ARGV — the one\n * provenance `repo/authored/paths.ts` says must never reach a path join unchecked.\n *\n * `livingCapabilityPaths` takes a plain `string`, and its comment states why: a\n * capability id has no grammar of its own — it is a YAML key and a chain of\n * directory names — so what holds that join is PROVENANCE. Every id it sees\n * today came out of a `readCapabilityTree` walk, where each component is a\n * `readdir` entry name and therefore demonstrably a directory that exists.\n * (`featureCapabilityDeltasDir` beside it takes a branded `FeatureDir` and no\n * capability id at all; the feature-side join is spelled by `loam new` itself.)\n *\n * `loam new --capability <id>` is the first caller with no such provenance. The\n * string is whatever a person typed, and it is joined as\n * `features/<dirName>/capabilities/<id>/spec.md` — so `--capability ../../evil`\n * resolves to `features/evil/spec.md`, a directory `listFeatures` then\n * enumerates as a feature, and one `..` further reaches the docs-repo root.\n * `resolveInside` cannot refuse either: both are still inside the repo. That is\n * the failure this module closes, and it is the same one\n * `core/kernel/ids/service.ts` opens with one file over.\n *\n * IT IS THE DIRECTORY-NAME RULE, NOT A NEW ONE. Nesting is spelled by the tree\n * (`payments/refunds` is two directories), so an id is legal exactly when every\n * one of its `/`-separated segments is a legal directory name in the docs repo\n * — `dirNameHazard`, the same check a service id and a subsystem name are held\n * to, Windows device names and trailing dots included. A capability tree that\n * only clones onto some of the machines that read it is the same defect there\n * as it is under `services/`.\n *\n * NO BRAND, deliberately, and the reason is the provenance rule above rather\n * than an omission: a brand is worth its annotations when a path builder can\n * DEMAND it, and neither builder can — both are called with walk-derived ids\n * that were never parsed here and must keep working. What this module gives is\n * the check the argv path owes; the rule that walk-derived ids are safe stays\n * where `repo/paths.ts` already writes it down.\n */\nimport { dirNameHazard } from \"./service.js\";\n\n/** Prose form of the rule, shown verbatim in the refusal so the fix is obvious. */\nconst CAPABILITY_ID_RULE =\n \"a capability id is one or more '/'-separated segments, each starting with a letter or digit and \" +\n \"containing only letters, digits, '.', '_' or '-' (no '..', no spaces, no leading or trailing '/') — \" +\n \"it becomes the capabilities/<id>/ directory chain, one directory per segment\";\n\n/**\n * Why `id` cannot be a capability id, or null when it can.\n *\n * A sentence rather than a boolean, matching `serviceIdProblem`: a caller that\n * is collecting findings prints exactly what a refusal would.\n */\nexport function capabilityIdProblem(id: unknown, label = \"--capability\"): string | null {\n if (typeof id !== \"string\" || id === \"\") {\n return `${label} must name a capability: ${CAPABILITY_ID_RULE}.`;\n }\n for (const segment of id.split(\"/\")) {\n const hazard = dirNameHazard(segment);\n if (hazard === null) continue;\n switch (hazard.kind) {\n case \"grammar\":\n return `Invalid ${label} '${id}': ${CAPABILITY_ID_RULE}.`;\n // Both Windows refusals keep their own sentence for `serviceIdProblem`'s\n // reason: \"does not match the grammar\" is true and useless, because the\n // id LOOKS fine and the problem is what the filesystem does with it.\n case \"windows-device\":\n return (\n `Invalid ${label} '${id}': '${hazard.stem}' is a reserved device name on Windows — ` +\n `the capabilities/${id}/ directory cannot be created there at all, so the docs repo would only work on some of the machines that clone it.`\n );\n case \"windows-trailing\":\n return (\n `Invalid ${label} '${id}': no segment of a capability id may end with '.' or a space — ` +\n \"Windows strips both when creating a directory, so the id would silently name one directory there and another everywhere else.\"\n );\n }\n }\n return null;\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** The only constructor. `resolved` must carry `DocsDir`'s provenance, above. */
|
|
2
|
+
export function docsDirOf(resolved) {
|
|
3
|
+
return resolved;
|
|
4
|
+
}
|
|
5
|
+
/** The only constructor. `abs` must carry `FeatureDir`'s provenance, above. */
|
|
6
|
+
export function featureDirOf(abs) {
|
|
7
|
+
return abs;
|
|
8
|
+
}
|
|
9
|
+
/** The only constructor. `abs` must carry one of `ServiceDir`'s two provenances, above. */
|
|
10
|
+
export function serviceDirOf(abs) {
|
|
11
|
+
return abs;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Where a service sits under `services/`, repo-relative and spelled from its
|
|
15
|
+
* own placement chain — `services/<subsystem>/…/<id>/` for a filed service,
|
|
16
|
+
* `services/<id>/` for an unfiled one.
|
|
17
|
+
*
|
|
18
|
+
* The join it replaces is `services/${id}`, which is right for an unfiled
|
|
19
|
+
* service and WRONG for every filed one, and the failure is the kind a reader
|
|
20
|
+
* cannot diagnose: the message names a directory that does not exist, so the
|
|
21
|
+
* fix goes to the wrong place, or a `cd` simply fails. Three findings already
|
|
22
|
+
* spelled it correctly by hand and `loam status` spelled it by id; this exists
|
|
23
|
+
* so the rule has one implementation rather than a convention.
|
|
24
|
+
*
|
|
25
|
+
* DISPLAY only, and not a `ServiceDir`: it is repo-relative text for a human
|
|
26
|
+
* to read, while `ServiceDir` is the absolute path artifacts resolve from. And
|
|
27
|
+
* `subsystem` is placement carrying no identity, so nothing here may be joined
|
|
28
|
+
* on — the id is the identity, at whatever depth it lives.
|
|
29
|
+
*/
|
|
30
|
+
export function serviceTreePath(s) {
|
|
31
|
+
return ["services", ...s.subsystem, s.id].join("/");
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=dirs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dirs.js","sourceRoot":"","sources":["../../../../src/core/kernel/ids/dirs.ts"],"names":[],"mappings":"AAwDA,iFAAiF;AACjF,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,OAAO,QAAmB,CAAC;AAC7B,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,GAAiB,CAAC;AAC3B,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,GAAiB,CAAC;AAC3B,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,eAAe,CAAC,CAAsC;IACpE,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC","sourcesContent":["/**\n * The two directory roots loam's path builders spell paths under, branded by\n * PROVENANCE — where the string came from — exactly as the id brands beside\n * this file are.\n *\n * `repo/paths.ts` interpolates caller-controlled ids into these roots, and its\n * guarantee is only as good as the root it starts from: an\n * `unfiledServicePaths(dir, id)` whose `dir` is whatever string happened to\n * be in scope spells paths\n * into whatever tree that string names. Honestly stated: these constructors\n * VALIDATE nothing — they RECORD a provenance the caller must already have\n * established, and what the gate contributes is that the cast appears in\n * exactly one place per brand (a cast anywhere else in `src/` is a finding,\n * not a shortcut). `init` brands its `--docs` resolution before its guards\n * run and refuses before any path is built — the provenance comment is the\n * contract those call sites are held to by review, not by the compiler.\n *\n * The two brands are deliberately disjoint: a docs repo root and a feature\n * directory are different trees, and `featurePaths(docsDir)` is precisely the\n * confusion the compiler should refuse.\n */\ndeclare const provenance: unique symbol;\n\n/**\n * The resolved absolute root of the shared docs repo. Provenance: resolved\n * through a loaded config (`parseConfig` resolves the stored spelling against\n * the config file's own directory) or an explicit `--docs` a command\n * validated. NOT the stored spelling — loam.json deliberately keeps the\n * relative form (`storedDocsDir`), and that spelling stays a plain `string`.\n */\nexport type DocsDir = string & { readonly [provenance]: \"docs-dir\" };\n\n/**\n * The absolute path of one feature's directory. Provenance: an enumeration\n * read this directory — `listFeatures` joined a name `readdir` returned onto\n * the features root it walked, so the directory demonstrably exists (or did,\n * one readdir ago).\n */\nexport type FeatureDir = string & { readonly [provenance]: \"feature-dir\" };\n\n/**\n * The absolute path of one service's directory — the value the artifact paths\n * in `repo/paths.ts` are spelled under. Two provenances, both stated because\n * both are load-bearing: the tree walk read it (`core/repo/tree/walk.ts`\n * joined a name `readdir` returned onto the directory it was listed under, at\n * whatever depth the service actually lives), or it is the canonical UNFILED\n * spelling `<docsDir>/services/<id>/` that `unfiledServicePaths` builds from a\n * validated id and a `DocsDir` — the one place creation lands, and the honest\n * fallback for a service the enumeration does not answer. A path from a\n * readdir is NARROWER than one built from a validated string: the directory\n * demonstrably exists where the path says. Nothing else may construct this —\n * in particular not a bare `join(docsDir, \"services\", name)` at a call site,\n * which is exactly the root-level assumption the subsystem tree retired.\n */\nexport type ServiceDir = string & { readonly [provenance]: \"service-dir\" };\n\n/** The only constructor. `resolved` must carry `DocsDir`'s provenance, above. */\nexport function docsDirOf(resolved: string): DocsDir {\n return resolved as DocsDir;\n}\n\n/** The only constructor. `abs` must carry `FeatureDir`'s provenance, above. */\nexport function featureDirOf(abs: string): FeatureDir {\n return abs as FeatureDir;\n}\n\n/** The only constructor. `abs` must carry one of `ServiceDir`'s two provenances, above. */\nexport function serviceDirOf(abs: string): ServiceDir {\n return abs as ServiceDir;\n}\n\n/**\n * Where a service sits under `services/`, repo-relative and spelled from its\n * own placement chain — `services/<subsystem>/…/<id>/` for a filed service,\n * `services/<id>/` for an unfiled one.\n *\n * The join it replaces is `services/${id}`, which is right for an unfiled\n * service and WRONG for every filed one, and the failure is the kind a reader\n * cannot diagnose: the message names a directory that does not exist, so the\n * fix goes to the wrong place, or a `cd` simply fails. Three findings already\n * spelled it correctly by hand and `loam status` spelled it by id; this exists\n * so the rule has one implementation rather than a convention.\n *\n * DISPLAY only, and not a `ServiceDir`: it is repo-relative text for a human\n * to read, while `ServiceDir` is the absolute path artifacts resolve from. And\n * `subsystem` is placement carrying no identity, so nothing here may be joined\n * on — the id is the identity, at whatever depth it lives.\n */\nexport function serviceTreePath(s: { id: string; subsystem: string[] }): string {\n return [\"services\", ...s.subsystem, s.id].join(\"/\");\n}\n"]}
|