@ybotok/loam 0.1.0-beta.3 → 0.2.0-alpha.2
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 +2635 -86
- 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 +599 -0
- package/SCHEMA.md +2355 -256
- 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 +323 -0
- package/dist/commands/archive/plan/gate.js.map +1 -0
- package/dist/commands/archive/plan/landscape.js +180 -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 +252 -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 +72 -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 +204 -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 +270 -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 +251 -0
- package/dist/commands/validate/service/service.js.map +1 -0
- package/dist/commands/validate/service/specs.js +251 -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/{agents-stamp.js → agent/agents-stamp.js} +50 -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 +337 -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.js → asyncapi/read.js} +95 -109
- 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 +48 -0
- package/dist/core/brief/unchecked.js.map +1 -0
- package/dist/core/c4/arch.js +174 -0
- package/dist/core/c4/arch.js.map +1 -0
- package/dist/core/c4/likec4.js +90 -0
- package/dist/core/c4/likec4.js.map +1 -0
- package/dist/core/c4/parsed/deployment.js +158 -0
- package/dist/core/c4/parsed/deployment.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js +132 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
- package/dist/core/c4/parsed/specification.js +78 -0
- package/dist/core/c4/parsed/specification.js.map +1 -0
- package/dist/core/c4/parsed/values.js +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 +187 -0
- package/dist/core/c4/project/load.js.map +1 -0
- package/dist/core/c4/project/staged.js +220 -0
- package/dist/core/c4/project/staged.js.map +1 -0
- package/dist/core/c4/resolve/service.js +159 -0
- package/dist/core/c4/resolve/service.js.map +1 -0
- package/dist/core/c4/resolve/steps.js +144 -0
- package/dist/core/c4/resolve/steps.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 +139 -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 +174 -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 +287 -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/deployment/delta.js +188 -0
- package/dist/core/deployment/delta.js.map +1 -0
- package/dist/core/deployment/objects.js +145 -0
- package/dist/core/deployment/objects.js.map +1 -0
- package/dist/core/diff/base-git.js +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 +7 -64
- 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 +120 -78
- 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 +137 -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 +210 -0
- package/dist/core/repo/authored/paths.js.map +1 -0
- package/dist/core/repo/entries.js +126 -0
- package/dist/core/repo/entries.js.map +1 -0
- package/dist/core/repo/paths.js +277 -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 +196 -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 +197 -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 +72 -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 +58 -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 -295
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/migrate-openspec.js +0 -950
- package/dist/commands/migrate-openspec.js.map +0 -1
- package/dist/commands/new.js +0 -413
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/rebase.js +0 -356
- package/dist/commands/rebase.js.map +0 -1
- package/dist/commands/show.js.map +0 -1
- package/dist/commands/status.js +0 -262
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/unarchive.js.map +0 -1
- package/dist/commands/validate.js +0 -1773
- package/dist/commands/validate.js.map +0 -1
- package/dist/commands/verify.js +0 -775
- package/dist/commands/verify.js.map +0 -1
- package/dist/commands/vouch.js +0 -421
- package/dist/commands/vouch.js.map +0 -1
- package/dist/core/agent.js +0 -2173
- package/dist/core/agent.js.map +0 -1
- package/dist/core/agents-stamp.js.map +0 -1
- package/dist/core/arch.js +0 -130
- package/dist/core/arch.js.map +0 -1
- package/dist/core/asyncapi.js.map +0 -1
- package/dist/core/brief.js +0 -755
- package/dist/core/brief.js.map +0 -1
- package/dist/core/coherence.js +0 -577
- package/dist/core/coherence.js.map +0 -1
- package/dist/core/concurrency.js.map +0 -1
- package/dist/core/config.js.map +0 -1
- package/dist/core/delta.js +0 -476
- package/dist/core/delta.js.map +0 -1
- package/dist/core/dependencies.js +0 -356
- package/dist/core/dependencies.js.map +0 -1
- package/dist/core/doctor.js +0 -596
- package/dist/core/doctor.js.map +0 -1
- package/dist/core/document-bytes.js.map +0 -1
- package/dist/core/explore.js.map +0 -1
- package/dist/core/frontmatter.js.map +0 -1
- package/dist/core/gherkin.js +0 -463
- package/dist/core/gherkin.js.map +0 -1
- package/dist/core/health.js +0 -67
- package/dist/core/health.js.map +0 -1
- package/dist/core/ids.js +0 -122
- package/dist/core/ids.js.map +0 -1
- package/dist/core/issue.js +0 -9
- package/dist/core/issue.js.map +0 -1
- package/dist/core/json.js +0 -74
- package/dist/core/json.js.map +0 -1
- package/dist/core/likec4.js +0 -489
- package/dist/core/likec4.js.map +0 -1
- package/dist/core/maturity.js +0 -66
- package/dist/core/maturity.js.map +0 -1
- package/dist/core/openapi-merge.js +0 -552
- package/dist/core/openapi-merge.js.map +0 -1
- package/dist/core/openapi.js.map +0 -1
- package/dist/core/openspec-inventory.js +0 -1440
- package/dist/core/openspec-inventory.js.map +0 -1
- package/dist/core/path-safety.js.map +0 -1
- package/dist/core/provenance.js +0 -787
- package/dist/core/provenance.js.map +0 -1
- package/dist/core/records.js.map +0 -1
- package/dist/core/repo.js +0 -438
- package/dist/core/repo.js.map +0 -1
- package/dist/core/report.js.map +0 -1
- package/dist/core/spec.js +0 -505
- package/dist/core/spec.js.map +0 -1
- package/dist/core/staging.js +0 -890
- package/dist/core/staging.js.map +0 -1
- package/dist/core/status.js +0 -1132
- package/dist/core/status.js.map +0 -1
- package/dist/core/steps.js +0 -72
- package/dist/core/steps.js.map +0 -1
- package/dist/core/verify.js +0 -807
- package/dist/core/verify.js.map +0 -1
- package/dist/core/version.js.map +0 -1
- /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
- /package/dist/core/{records.js → kernel/records.js} +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The subsystem surface's git questions, beside the provenance ones
|
|
3
|
+
* (`../git.ts`) whose timeout and capped stdout reader they share. A package
|
|
4
|
+
* of their own because `git.ts` sits against the 300-line limit and these
|
|
5
|
+
* three are one subject: what a `subsystem move` must know before renaming
|
|
6
|
+
* (`gitDirtyPaths`), what `subsystem history` asks after (`gitRenameHops`),
|
|
7
|
+
* and the ONE write the roadmap blesses (`gitStageRenames` — "move stages
|
|
8
|
+
* renames without committing"). The doctrine is unchanged: every way git can
|
|
9
|
+
* decline reads as "git will not say", never as an error a caller must
|
|
10
|
+
* handle.
|
|
11
|
+
*/
|
|
12
|
+
import { spawn } from "node:child_process";
|
|
13
|
+
import { collectStdout, GIT_TIMEOUT_MS } from "../git.js";
|
|
14
|
+
/**
|
|
15
|
+
* Which of these repo-relative paths carry UNCOMMITTED work — anything
|
|
16
|
+
* `git status --porcelain` reports beneath them, untracked files included: an
|
|
17
|
+
* untracked file is exactly a change no commit records. Null for every way
|
|
18
|
+
* git declines (not a repository, not installed, a timeout, a non-zero exit),
|
|
19
|
+
* and the one caller — `subsystem move`'s only refusal — treats null as
|
|
20
|
+
* "proceed": a refusal needs positive evidence, and a fleet that does not use
|
|
21
|
+
* git must not be refused a move over it.
|
|
22
|
+
*
|
|
23
|
+
* `-z` for the same reason `gitTrackedFiles` uses it: line output QUOTES
|
|
24
|
+
* paths with spaces or non-ASCII bytes. A rename entry (`R ` when something
|
|
25
|
+
* is already staged) carries a second NUL-separated path; both belong in the
|
|
26
|
+
* answer.
|
|
27
|
+
*/
|
|
28
|
+
export async function gitDirtyPaths(repoDir, rels) {
|
|
29
|
+
if (rels.length === 0)
|
|
30
|
+
return [];
|
|
31
|
+
return new Promise((done) => {
|
|
32
|
+
const child = spawn("git", ["status", "--porcelain", "-z", "--", ...rels], {
|
|
33
|
+
cwd: repoDir,
|
|
34
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
35
|
+
timeout: GIT_TIMEOUT_MS,
|
|
36
|
+
});
|
|
37
|
+
const out = collectStdout(child);
|
|
38
|
+
child.on("error", () => done(null));
|
|
39
|
+
child.on("close", (code) => {
|
|
40
|
+
if (code !== 0 || out.overflowed()) {
|
|
41
|
+
done(null);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const tokens = out.text().split("\0").filter((t) => t !== "");
|
|
45
|
+
const paths = [];
|
|
46
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
47
|
+
const entry = tokens[i];
|
|
48
|
+
// "XY <path>" — two status letters and a space. A rename/copy entry's
|
|
49
|
+
// ORIGINAL path follows as its own NUL token; take it too.
|
|
50
|
+
paths.push(entry.slice(3));
|
|
51
|
+
if (entry.startsWith("R") || entry.startsWith("C")) {
|
|
52
|
+
i += 1;
|
|
53
|
+
if (tokens[i] !== undefined)
|
|
54
|
+
paths.push(tokens[i]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
done(paths);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* How this file moved over time, OLDEST hop first — or null when git will not
|
|
63
|
+
* say. The question follows one FILE deliberately: `git log --follow` is
|
|
64
|
+
* defined for a single file and prints NOTHING for a directory path, so
|
|
65
|
+
* `subsystem history` follows a representative file inside the directory it
|
|
66
|
+
* is asked about (a service's spec.md, a subsystem's marker) and reads each
|
|
67
|
+
* hop's directories off the file's own rename record. An empty array is git
|
|
68
|
+
* ANSWERING "never renamed"; null is git declining — the caller renders the
|
|
69
|
+
* two differently and exits 0 for both, per the doctrine above.
|
|
70
|
+
*
|
|
71
|
+
* Output shape under `-z --format=%H`: `<hash>\0\nR<score>\0<old>\0<new>\0…`,
|
|
72
|
+
* newest commit first — the token walk below is anchored on the two shapes a
|
|
73
|
+
* token can have (a 40-hex hash, an R-status) so an unexpected shape yields
|
|
74
|
+
* fewer hops, never a wrong pairing.
|
|
75
|
+
*/
|
|
76
|
+
export async function gitRenameHops(repoDir, rel) {
|
|
77
|
+
return new Promise((done) => {
|
|
78
|
+
const child = spawn("git", ["log", "--follow", "--diff-filter=R", "--name-status", "-z", "--format=%H", "--", rel], { cwd: repoDir, stdio: ["ignore", "pipe", "ignore"], timeout: GIT_TIMEOUT_MS });
|
|
79
|
+
const out = collectStdout(child);
|
|
80
|
+
child.on("error", () => done(null));
|
|
81
|
+
child.on("close", (code) => {
|
|
82
|
+
if (code !== 0 || out.overflowed()) {
|
|
83
|
+
done(null);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const tokens = out.text().split("\0").map((t) => (t.startsWith("\n") ? t.slice(1) : t));
|
|
87
|
+
const hops = [];
|
|
88
|
+
let commit = "";
|
|
89
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
90
|
+
const token = tokens[i];
|
|
91
|
+
if (/^[0-9a-f]{40}$/.test(token))
|
|
92
|
+
commit = token;
|
|
93
|
+
else if (/^R\d*$/.test(token) && tokens[i + 1] !== undefined && tokens[i + 2] !== undefined) {
|
|
94
|
+
hops.push({ commit, from: tokens[i + 1], to: tokens[i + 2] });
|
|
95
|
+
i += 2;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
done(hops.reverse());
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Was the rename record that produced a hop into `dest` at `commit` a GUESS?
|
|
104
|
+
* `git log --follow` pairs old and new paths by CONTENT similarity, and two
|
|
105
|
+
* identical files moved (or one moved while its twin is created or deleted)
|
|
106
|
+
* in the same commit give it a coin to flip — subsystem markers are routinely
|
|
107
|
+
* byte-identical (often empty), so a multi-directory move can report a hop
|
|
108
|
+
* from a directory the followed file never occupied. The cross-check asks the
|
|
109
|
+
* commit itself: if two or more of its diff entries carry the same blob as
|
|
110
|
+
* the one `dest` received, the pairing chose between equals and the hop is
|
|
111
|
+
* not evidence. `false` = unambiguous; `true` = a guess; null = git will not
|
|
112
|
+
* say — and the caller must treat BOTH non-false answers as "will not say",
|
|
113
|
+
* because repeating a maybe-phantom hop is worse than answering nothing.
|
|
114
|
+
*/
|
|
115
|
+
export async function gitRenamePairingAmbiguous(repoDir, commit, dest) {
|
|
116
|
+
return new Promise((done) => {
|
|
117
|
+
const child = spawn("git", ["diff-tree", "-r", "-M", "-z", commit], {
|
|
118
|
+
cwd: repoDir,
|
|
119
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
120
|
+
timeout: GIT_TIMEOUT_MS,
|
|
121
|
+
});
|
|
122
|
+
const out = collectStdout(child);
|
|
123
|
+
child.on("error", () => done(null));
|
|
124
|
+
child.on("close", (code) => {
|
|
125
|
+
if (code !== 0 || out.overflowed()) {
|
|
126
|
+
done(null);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
// `-z` raw form: `:<mode> <mode> <sha> <sha> <status>` NUL, then one
|
|
130
|
+
// path token — two for a rename/copy. The leading token is the commit.
|
|
131
|
+
const tokens = out.text().split("\0").filter((t) => t !== "");
|
|
132
|
+
const entries = [];
|
|
133
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
134
|
+
const token = tokens[i];
|
|
135
|
+
if (!token.startsWith(":"))
|
|
136
|
+
continue;
|
|
137
|
+
const fields = token.slice(1).split(" ");
|
|
138
|
+
const status = fields[4] ?? "";
|
|
139
|
+
const paths = status.startsWith("R") || status.startsWith("C") ? [tokens[i + 1], tokens[i + 2]] : [tokens[i + 1]];
|
|
140
|
+
entries.push({
|
|
141
|
+
srcSha: fields[2] ?? "",
|
|
142
|
+
dstSha: fields[3] ?? "",
|
|
143
|
+
status,
|
|
144
|
+
paths: paths.filter((p) => p !== undefined),
|
|
145
|
+
});
|
|
146
|
+
i += paths.length;
|
|
147
|
+
}
|
|
148
|
+
const own = entries.find((e) => e.status.startsWith("R") && e.paths[1] === dest);
|
|
149
|
+
if (own === undefined) {
|
|
150
|
+
// The hop's own rename is not in the commit's record — an answer this
|
|
151
|
+
// check cannot ground is an answer git will not give.
|
|
152
|
+
done(null);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const twins = entries.filter((e) => e.srcSha === own.dstSha || e.dstSha === own.dstSha);
|
|
156
|
+
done(twins.length > 1);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Best-effort `git add -A` over these repo-relative paths — the ONE write
|
|
162
|
+
* (module banner): after a `subsystem move` lands its renames, staging the
|
|
163
|
+
* delete-side and add-side together is what lets git record the pair as a
|
|
164
|
+
* rename, which is what keeps `subsystem history` answerable after the user
|
|
165
|
+
* commits. Failure-silent in every direction: a fleet without git, an index
|
|
166
|
+
* lock, a timeout — the move already succeeded, and nothing here may turn it
|
|
167
|
+
* into an error.
|
|
168
|
+
*/
|
|
169
|
+
export async function gitStageRenames(repoDir, rels) {
|
|
170
|
+
if (rels.length === 0)
|
|
171
|
+
return;
|
|
172
|
+
return new Promise((done) => {
|
|
173
|
+
const child = spawn("git", ["add", "-A", "--", ...rels], {
|
|
174
|
+
cwd: repoDir,
|
|
175
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
176
|
+
timeout: GIT_TIMEOUT_MS,
|
|
177
|
+
});
|
|
178
|
+
child.on("error", () => done());
|
|
179
|
+
child.on("close", () => done());
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=moves.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moves.js","sourceRoot":"","sources":["../../../../src/core/provenance/gitq/moves.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The subsystem surface's git questions, beside the provenance ones\n * (`../git.ts`) whose timeout and capped stdout reader they share. A package\n * of their own because `git.ts` sits against the 300-line limit and these\n * three are one subject: what a `subsystem move` must know before renaming\n * (`gitDirtyPaths`), what `subsystem history` asks after (`gitRenameHops`),\n * and the ONE write the roadmap blesses (`gitStageRenames` — \"move stages\n * renames without committing\"). The doctrine is unchanged: every way git can\n * decline reads as \"git will not say\", never as an error a caller must\n * handle.\n */\nimport { spawn } from \"node:child_process\";\nimport { collectStdout, GIT_TIMEOUT_MS } from \"../git.js\";\n\n/**\n * Which of these repo-relative paths carry UNCOMMITTED work — anything\n * `git status --porcelain` reports beneath them, untracked files included: an\n * untracked file is exactly a change no commit records. Null for every way\n * git declines (not a repository, not installed, a timeout, a non-zero exit),\n * and the one caller — `subsystem move`'s only refusal — treats null as\n * \"proceed\": a refusal needs positive evidence, and a fleet that does not use\n * git must not be refused a move over it.\n *\n * `-z` for the same reason `gitTrackedFiles` uses it: line output QUOTES\n * paths with spaces or non-ASCII bytes. A rename entry (`R ` when something\n * is already staged) carries a second NUL-separated path; both belong in the\n * answer.\n */\nexport async function gitDirtyPaths(repoDir: string, rels: string[]): Promise<string[] | null> {\n if (rels.length === 0) return [];\n return new Promise<string[] | null>((done) => {\n const child = spawn(\"git\", [\"status\", \"--porcelain\", \"-z\", \"--\", ...rels], {\n cwd: repoDir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: GIT_TIMEOUT_MS,\n });\n const out = collectStdout(child);\n child.on(\"error\", () => done(null));\n child.on(\"close\", (code) => {\n if (code !== 0 || out.overflowed()) {\n done(null);\n return;\n }\n const tokens = out.text().split(\"\\0\").filter((t) => t !== \"\");\n const paths: string[] = [];\n for (let i = 0; i < tokens.length; i += 1) {\n const entry = tokens[i]!;\n // \"XY <path>\" — two status letters and a space. A rename/copy entry's\n // ORIGINAL path follows as its own NUL token; take it too.\n paths.push(entry.slice(3));\n if (entry.startsWith(\"R\") || entry.startsWith(\"C\")) {\n i += 1;\n if (tokens[i] !== undefined) paths.push(tokens[i]!);\n }\n }\n done(paths);\n });\n });\n}\n\n/** One hop of a path's rename history: the commit, and the path either side of it. */\nexport interface RenameHop {\n commit: string;\n from: string;\n to: string;\n}\n\n/**\n * How this file moved over time, OLDEST hop first — or null when git will not\n * say. The question follows one FILE deliberately: `git log --follow` is\n * defined for a single file and prints NOTHING for a directory path, so\n * `subsystem history` follows a representative file inside the directory it\n * is asked about (a service's spec.md, a subsystem's marker) and reads each\n * hop's directories off the file's own rename record. An empty array is git\n * ANSWERING \"never renamed\"; null is git declining — the caller renders the\n * two differently and exits 0 for both, per the doctrine above.\n *\n * Output shape under `-z --format=%H`: `<hash>\\0\\nR<score>\\0<old>\\0<new>\\0…`,\n * newest commit first — the token walk below is anchored on the two shapes a\n * token can have (a 40-hex hash, an R-status) so an unexpected shape yields\n * fewer hops, never a wrong pairing.\n */\nexport async function gitRenameHops(repoDir: string, rel: string): Promise<RenameHop[] | null> {\n return new Promise<RenameHop[] | null>((done) => {\n const child = spawn(\n \"git\",\n [\"log\", \"--follow\", \"--diff-filter=R\", \"--name-status\", \"-z\", \"--format=%H\", \"--\", rel],\n { cwd: repoDir, stdio: [\"ignore\", \"pipe\", \"ignore\"], timeout: GIT_TIMEOUT_MS },\n );\n const out = collectStdout(child);\n child.on(\"error\", () => done(null));\n child.on(\"close\", (code) => {\n if (code !== 0 || out.overflowed()) {\n done(null);\n return;\n }\n const tokens = out.text().split(\"\\0\").map((t) => (t.startsWith(\"\\n\") ? t.slice(1) : t));\n const hops: RenameHop[] = [];\n let commit = \"\";\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i]!;\n if (/^[0-9a-f]{40}$/.test(token)) commit = token;\n else if (/^R\\d*$/.test(token) && tokens[i + 1] !== undefined && tokens[i + 2] !== undefined) {\n hops.push({ commit, from: tokens[i + 1]!, to: tokens[i + 2]! });\n i += 2;\n }\n }\n done(hops.reverse());\n });\n });\n}\n\n/**\n * Was the rename record that produced a hop into `dest` at `commit` a GUESS?\n * `git log --follow` pairs old and new paths by CONTENT similarity, and two\n * identical files moved (or one moved while its twin is created or deleted)\n * in the same commit give it a coin to flip — subsystem markers are routinely\n * byte-identical (often empty), so a multi-directory move can report a hop\n * from a directory the followed file never occupied. The cross-check asks the\n * commit itself: if two or more of its diff entries carry the same blob as\n * the one `dest` received, the pairing chose between equals and the hop is\n * not evidence. `false` = unambiguous; `true` = a guess; null = git will not\n * say — and the caller must treat BOTH non-false answers as \"will not say\",\n * because repeating a maybe-phantom hop is worse than answering nothing.\n */\nexport async function gitRenamePairingAmbiguous(\n repoDir: string,\n commit: string,\n dest: string,\n): Promise<boolean | null> {\n return new Promise<boolean | null>((done) => {\n const child = spawn(\"git\", [\"diff-tree\", \"-r\", \"-M\", \"-z\", commit], {\n cwd: repoDir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: GIT_TIMEOUT_MS,\n });\n const out = collectStdout(child);\n child.on(\"error\", () => done(null));\n child.on(\"close\", (code) => {\n if (code !== 0 || out.overflowed()) {\n done(null);\n return;\n }\n // `-z` raw form: `:<mode> <mode> <sha> <sha> <status>` NUL, then one\n // path token — two for a rename/copy. The leading token is the commit.\n const tokens = out.text().split(\"\\0\").filter((t) => t !== \"\");\n const entries: { srcSha: string; dstSha: string; status: string; paths: string[] }[] = [];\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i]!;\n if (!token.startsWith(\":\")) continue;\n const fields = token.slice(1).split(\" \");\n const status = fields[4] ?? \"\";\n const paths = status.startsWith(\"R\") || status.startsWith(\"C\") ? [tokens[i + 1], tokens[i + 2]] : [tokens[i + 1]];\n entries.push({\n srcSha: fields[2] ?? \"\",\n dstSha: fields[3] ?? \"\",\n status,\n paths: paths.filter((p): p is string => p !== undefined),\n });\n i += paths.length;\n }\n const own = entries.find((e) => e.status.startsWith(\"R\") && e.paths[1] === dest);\n if (own === undefined) {\n // The hop's own rename is not in the commit's record — an answer this\n // check cannot ground is an answer git will not give.\n done(null);\n return;\n }\n const twins = entries.filter((e) => e.srcSha === own.dstSha || e.dstSha === own.dstSha);\n done(twins.length > 1);\n });\n });\n}\n\n/**\n * Best-effort `git add -A` over these repo-relative paths — the ONE write\n * (module banner): after a `subsystem move` lands its renames, staging the\n * delete-side and add-side together is what lets git record the pair as a\n * rename, which is what keeps `subsystem history` answerable after the user\n * commits. Failure-silent in every direction: a fleet without git, an index\n * lock, a timeout — the move already succeeded, and nothing here may turn it\n * into an error.\n */\nexport async function gitStageRenames(repoDir: string, rels: string[]): Promise<void> {\n if (rels.length === 0) return;\n return new Promise<void>((done) => {\n const child = spawn(\"git\", [\"add\", \"-A\", \"--\", ...rels], {\n cwd: repoDir,\n stdio: [\"ignore\", \"ignore\", \"ignore\"],\n timeout: GIT_TIMEOUT_MS,\n });\n child.on(\"error\", () => done());\n child.on(\"close\", () => done());\n });\n}\n"]}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doc-body ancestry in the DOCS repo — the two git questions behind
|
|
3
|
+
* `loam vouch --pack`'s body delta: which commit last held the body a person
|
|
4
|
+
* vouched for (`findVouchedAncestor`), and what the file's diff from that
|
|
5
|
+
* commit to the working tree says (`bodyDiffText`). Read-only throughout:
|
|
6
|
+
* `rev-parse`/`log`/`show`/`diff`, no checkout, no temp writes.
|
|
7
|
+
*
|
|
8
|
+
* The mechanics are `core/diff/base-git.ts`'s to the letter — literally so:
|
|
9
|
+
* both import the one `gitText`/`GitAnswer`/`declineDetail` set from
|
|
10
|
+
* `../git.js`, so a hardening lands in both or in neither — but the doctrine
|
|
11
|
+
* is deliberately the OPPOSITE consequence. `loam diff` REFUSES when git
|
|
12
|
+
* cannot answer, because a silently empty base would report the whole fleet
|
|
13
|
+
* as added — a lie with a green exit code. The pack DEGRADES to "full read"
|
|
14
|
+
* instead: its output is a reading list, and for a reading list the
|
|
15
|
+
* conservative direction — read more, never less — is always honest, the same
|
|
16
|
+
* direction as provenance's existing hash-everything fallback. The shared
|
|
17
|
+
* helpers carry no verdict, so neither module can borrow the other's
|
|
18
|
+
* consequence by accident; each spells its own at its own call sites.
|
|
19
|
+
*
|
|
20
|
+
* The walk is BOUNDED at `ANCESTOR_WALK_CAP` commits. A vouched commit pushed
|
|
21
|
+
* past the cap by a very active docs repo degrades a legitimate incremental
|
|
22
|
+
* read to a full read — safe direction, and the `none` reason names the cap
|
|
23
|
+
* so the human understands why. Within the walk, one commit whose version of
|
|
24
|
+
* the file is absent (git answering "not here") is skipped; a git that stops
|
|
25
|
+
* answering mid-walk (spawn error, kill, timeout, output cap) aborts the whole
|
|
26
|
+
* search as `no-git` rather than letting 200 futile spawns each wait out the
|
|
27
|
+
* timeout.
|
|
28
|
+
*/
|
|
29
|
+
import { declineDetail, gitText } from "../git.js";
|
|
30
|
+
import { contentDigest } from "../stamp.js";
|
|
31
|
+
/**
|
|
32
|
+
* How many commits touching the file the ancestor search will read before
|
|
33
|
+
* declaring the vouched state unreachable. Past it the answer is `none` — a
|
|
34
|
+
* full read — with a reason that names the cap.
|
|
35
|
+
*/
|
|
36
|
+
const ANCESTOR_WALK_CAP = 200;
|
|
37
|
+
/**
|
|
38
|
+
* How much diff text the pack will print. A body diff past this is not a
|
|
39
|
+
* reading list any more — the honest answer is "read the file", as `failed`.
|
|
40
|
+
*/
|
|
41
|
+
const MAX_DIFF_TEXT_CHARS = 1024 * 1024;
|
|
42
|
+
/**
|
|
43
|
+
* The shared `gitText` (`../git.js`) with colour forced off. base-git's three
|
|
44
|
+
* questions (`rev-parse`/`ls-tree`/`show`) never colour, but this module runs
|
|
45
|
+
* `log` and `diff`, and a user's `color.ui = always` forces SGR escape
|
|
46
|
+
* sequences into their output even on a pipe — which would put terminal codes
|
|
47
|
+
* inside the `--json` payload's diff text and make the 40-hex-anchored log
|
|
48
|
+
* walk silently match nothing on exactly that machine. The flag lives here,
|
|
49
|
+
* not in the shared helper, because it is this module's need, not a property
|
|
50
|
+
* of asking git a question.
|
|
51
|
+
*/
|
|
52
|
+
async function gitPlain(docsDir, args) {
|
|
53
|
+
return gitText(docsDir, ["-c", "color.ui=false", ...args]);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The bytes this commit's blob would carry on a CRLF checkout. The stamped
|
|
57
|
+
* `content_digest` is byte-exact over the WORKING-TREE file (stamp.ts), while
|
|
58
|
+
* `git show` hands back the BLOB — and on a Windows checkout with
|
|
59
|
+
* `core.autocrlf=true` the blob is LF where every working-tree byte is CRLF,
|
|
60
|
+
* so the exact digest of the blob would never equal a stamp taken over the
|
|
61
|
+
* same content and every vouched ancestor would be invisible on exactly those
|
|
62
|
+
* machines. Same hazard, same direction as `core/verify/pins/pin.ts`'s
|
|
63
|
+
* `pinnedDigest`, resolved the inverse way because the stamp's recipe is
|
|
64
|
+
* published and cannot be re-normalized after the fact: instead of
|
|
65
|
+
* normalizing both sides, the search hashes each candidate twice — as the
|
|
66
|
+
* blob is, and as its CRLF checkout rendering — and a match on either is a
|
|
67
|
+
* match. Only lone LFs are rewritten, so a blob that already carries CRLFs
|
|
68
|
+
* (committed without the clean filter, hence byte-identical on disk) is
|
|
69
|
+
* covered by the exact hash.
|
|
70
|
+
*/
|
|
71
|
+
function checkoutRendering(text) {
|
|
72
|
+
return text.replace(/(?<!\r)\n/g, "\r\n");
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The last commit in the docs repo's history whose version of `rel` (a
|
|
76
|
+
* docs-relative, `/`-separated path) has a body hashing to the stamped
|
|
77
|
+
* `content_digest` — newest first, because the reading list wants the
|
|
78
|
+
* SHORTEST honest diff, and any older commit holding the same bytes yields
|
|
79
|
+
* the same one.
|
|
80
|
+
*/
|
|
81
|
+
export async function findVouchedAncestor(docsDir, rel, stampedDigest) {
|
|
82
|
+
// Belt to the callers' braces: `rel` is derived from loam's own path
|
|
83
|
+
// spelling, never argv, but every spelled pathspec below rides an argv, so
|
|
84
|
+
// an option-shaped or empty value is refused here rather than parsed by
|
|
85
|
+
// git as a flag. Same reason base-git.ts refuses option-shaped refs.
|
|
86
|
+
if (rel.trim() === "" || rel.startsWith("-")) {
|
|
87
|
+
return { kind: "no-git", detail: `'${rel}' is not a path git can be asked about.` };
|
|
88
|
+
}
|
|
89
|
+
// `--show-prefix` first, and not decoration (base-git.ts proved this): a
|
|
90
|
+
// docs repo checked out as a subdirectory of a larger repository answers
|
|
91
|
+
// `show` only under that prefix, and a walk whose paths all miss would
|
|
92
|
+
// report "no vouched ancestor" about a history that holds one.
|
|
93
|
+
const where = await gitPlain(docsDir, ["rev-parse", "--show-prefix"]);
|
|
94
|
+
if (where.spawnError !== undefined || where.code !== 0) {
|
|
95
|
+
return { kind: "no-git", detail: declineDetail(where) };
|
|
96
|
+
}
|
|
97
|
+
const prefix = where.out.trim();
|
|
98
|
+
// The pathspec is cwd-relative (no prefix — `log` resolves it against the
|
|
99
|
+
// cwd, unlike `show`'s repo-root spelling below). `-n` is the walk's bound.
|
|
100
|
+
const log = await gitPlain(docsDir, [
|
|
101
|
+
"log", "-n", String(ANCESTOR_WALK_CAP), "--format=%H", "--", rel,
|
|
102
|
+
]);
|
|
103
|
+
if (log.spawnError !== undefined || log.code !== 0 || log.overflowed) {
|
|
104
|
+
return { kind: "no-git", detail: declineDetail(log) };
|
|
105
|
+
}
|
|
106
|
+
// Only shas shaped like shas walk on — an unexpected line yields a shorter
|
|
107
|
+
// walk, never a malformed argv (the gitq/moves.ts token-anchoring rule).
|
|
108
|
+
const commits = log.out.split("\n").map((l) => l.trim()).filter((l) => /^[0-9a-f]{40}$/.test(l));
|
|
109
|
+
// Sequential on purpose: newest-first order is the answer's definition, and
|
|
110
|
+
// the walk stops at the first match — a fan-out would read history it does
|
|
111
|
+
// not need and could answer with an older commit than the newest match.
|
|
112
|
+
for (const commit of commits) {
|
|
113
|
+
const shown = await gitPlain(docsDir, ["show", `${commit}:${prefix}${rel}`]);
|
|
114
|
+
if (shown.spawnError !== undefined || shown.code === null) {
|
|
115
|
+
// Git stopped answering (not installed mid-walk, killed, timed out, or
|
|
116
|
+
// the output cap fired on a monstrous version). Abort rather than walk
|
|
117
|
+
// on: each further spawn could wait out the whole timeout, and a "none"
|
|
118
|
+
// built over commits nobody read would claim the history was searched.
|
|
119
|
+
return {
|
|
120
|
+
kind: "no-git",
|
|
121
|
+
detail: shown.overflowed
|
|
122
|
+
? `the file at ${commit.slice(0, 12)} exceeds the git output cap.`
|
|
123
|
+
: declineDetail(shown),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// A real non-zero exit is git ANSWERING — the file is absent at this
|
|
127
|
+
// commit (renamed since, or the commit only deleted it). Skip it.
|
|
128
|
+
if (shown.code !== 0)
|
|
129
|
+
continue;
|
|
130
|
+
if (contentDigest(shown.out) === stampedDigest ||
|
|
131
|
+
contentDigest(checkoutRendering(shown.out)) === stampedDigest) {
|
|
132
|
+
return { kind: "found", commit, text: shown.out };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
kind: "none",
|
|
137
|
+
reason: commits.length < ANCESTOR_WALK_CAP
|
|
138
|
+
? "body has no vouched ancestor in history"
|
|
139
|
+
: `body has no vouched ancestor in history — only the last ${ANCESTOR_WALK_CAP} commits touching the file were walked, and the vouched state may lie beyond them`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The file's diff from its last vouched state: `git diff <commit> -- <rel>`,
|
|
144
|
+
* commit-to-working-tree, in the docs repo. The output is git's own — when a
|
|
145
|
+
* hand edit touched the frontmatter after the vouch, its hunks ride along,
|
|
146
|
+
* which is why the pack's prose says "the file's diff", never "the body's".
|
|
147
|
+
*/
|
|
148
|
+
export async function bodyDiffText(docsDir, commit, rel) {
|
|
149
|
+
// The commit came out of `findVouchedAncestor`'s own regex-vetted walk, but
|
|
150
|
+
// this function is exported API: re-vet, so no future caller can place an
|
|
151
|
+
// option-shaped string in the one argv slot that is not behind `--`.
|
|
152
|
+
if (!/^[0-9a-f]{40}$/.test(commit)) {
|
|
153
|
+
return { kind: "failed", reason: `'${commit}' is not a commit this diff can be asked about.` };
|
|
154
|
+
}
|
|
155
|
+
if (rel.trim() === "" || rel.startsWith("-")) {
|
|
156
|
+
return { kind: "failed", reason: `'${rel}' is not a path git can be asked about.` };
|
|
157
|
+
}
|
|
158
|
+
// `--no-optional-locks`: a commit-to-worktree diff may opportunistically
|
|
159
|
+
// refresh `.git/index`'s stat cache, which is a WRITE — small, benign, and
|
|
160
|
+
// exactly what a command documented as "writes nothing" must not do. The
|
|
161
|
+
// global flag tells git to skip any operation that would take a lock it
|
|
162
|
+
// does not strictly need. `--no-ext-diff --no-textconv` close the rest of
|
|
163
|
+
// the config surface: `diff.external` / a `.gitattributes` diff driver make
|
|
164
|
+
// `git diff` EXECUTE a user-configured program instead of printing a diff —
|
|
165
|
+
// an arbitrary write hazard on a read-only path, and output no machine
|
|
166
|
+
// consumer of `body.diff` could parse. The pack's diff is a machine
|
|
167
|
+
// artifact, not a terminal rendering; git's own plumbing flags say so.
|
|
168
|
+
const answer = await gitPlain(docsDir, [
|
|
169
|
+
"--no-optional-locks", "diff", "--no-ext-diff", "--no-textconv", commit, "--", rel,
|
|
170
|
+
]);
|
|
171
|
+
if (answer.spawnError !== undefined || answer.code !== 0 || answer.overflowed) {
|
|
172
|
+
return { kind: "failed", reason: declineDetail(answer) };
|
|
173
|
+
}
|
|
174
|
+
if (answer.out.length > MAX_DIFF_TEXT_CHARS) {
|
|
175
|
+
return { kind: "failed", reason: "the body diff exceeds the pack's print cap — read the file" };
|
|
176
|
+
}
|
|
177
|
+
return { kind: "ok", diff: answer.out };
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=vouched-ancestor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vouched-ancestor.js","sourceRoot":"","sources":["../../../../src/core/provenance/gitq/vouched-ancestor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Doc-body ancestry in the DOCS repo — the two git questions behind\n * `loam vouch --pack`'s body delta: which commit last held the body a person\n * vouched for (`findVouchedAncestor`), and what the file's diff from that\n * commit to the working tree says (`bodyDiffText`). Read-only throughout:\n * `rev-parse`/`log`/`show`/`diff`, no checkout, no temp writes.\n *\n * The mechanics are `core/diff/base-git.ts`'s to the letter — literally so:\n * both import the one `gitText`/`GitAnswer`/`declineDetail` set from\n * `../git.js`, so a hardening lands in both or in neither — but the doctrine\n * is deliberately the OPPOSITE consequence. `loam diff` REFUSES when git\n * cannot answer, because a silently empty base would report the whole fleet\n * as added — a lie with a green exit code. The pack DEGRADES to \"full read\"\n * instead: its output is a reading list, and for a reading list the\n * conservative direction — read more, never less — is always honest, the same\n * direction as provenance's existing hash-everything fallback. The shared\n * helpers carry no verdict, so neither module can borrow the other's\n * consequence by accident; each spells its own at its own call sites.\n *\n * The walk is BOUNDED at `ANCESTOR_WALK_CAP` commits. A vouched commit pushed\n * past the cap by a very active docs repo degrades a legitimate incremental\n * read to a full read — safe direction, and the `none` reason names the cap\n * so the human understands why. Within the walk, one commit whose version of\n * the file is absent (git answering \"not here\") is skipped; a git that stops\n * answering mid-walk (spawn error, kill, timeout, output cap) aborts the whole\n * search as `no-git` rather than letting 200 futile spawns each wait out the\n * timeout.\n */\nimport { declineDetail, gitText, type GitAnswer } from \"../git.js\";\nimport { contentDigest } from \"../stamp.js\";\n\n/**\n * How many commits touching the file the ancestor search will read before\n * declaring the vouched state unreachable. Past it the answer is `none` — a\n * full read — with a reason that names the cap.\n */\nconst ANCESTOR_WALK_CAP = 200;\n\n/**\n * How much diff text the pack will print. A body diff past this is not a\n * reading list any more — the honest answer is \"read the file\", as `failed`.\n */\nconst MAX_DIFF_TEXT_CHARS = 1024 * 1024;\n\n/**\n * The shared `gitText` (`../git.js`) with colour forced off. base-git's three\n * questions (`rev-parse`/`ls-tree`/`show`) never colour, but this module runs\n * `log` and `diff`, and a user's `color.ui = always` forces SGR escape\n * sequences into their output even on a pipe — which would put terminal codes\n * inside the `--json` payload's diff text and make the 40-hex-anchored log\n * walk silently match nothing on exactly that machine. The flag lives here,\n * not in the shared helper, because it is this module's need, not a property\n * of asking git a question.\n */\nasync function gitPlain(docsDir: string, args: string[]): Promise<GitAnswer> {\n return gitText(docsDir, [\"-c\", \"color.ui=false\", ...args]);\n}\n\n/**\n * The bytes this commit's blob would carry on a CRLF checkout. The stamped\n * `content_digest` is byte-exact over the WORKING-TREE file (stamp.ts), while\n * `git show` hands back the BLOB — and on a Windows checkout with\n * `core.autocrlf=true` the blob is LF where every working-tree byte is CRLF,\n * so the exact digest of the blob would never equal a stamp taken over the\n * same content and every vouched ancestor would be invisible on exactly those\n * machines. Same hazard, same direction as `core/verify/pins/pin.ts`'s\n * `pinnedDigest`, resolved the inverse way because the stamp's recipe is\n * published and cannot be re-normalized after the fact: instead of\n * normalizing both sides, the search hashes each candidate twice — as the\n * blob is, and as its CRLF checkout rendering — and a match on either is a\n * match. Only lone LFs are rewritten, so a blob that already carries CRLFs\n * (committed without the clean filter, hence byte-identical on disk) is\n * covered by the exact hash.\n */\nfunction checkoutRendering(text: string): string {\n return text.replace(/(?<!\\r)\\n/g, \"\\r\\n\");\n}\n\nexport type AncestorSearch =\n | {\n kind: \"found\";\n commit: string;\n /** The file exactly as that commit holds it — what the section delta reads. */\n text: string;\n }\n /** The walk completed and nothing matched — the honest verdict is a full read. */\n | { kind: \"none\"; reason: string }\n /** Git declined somewhere the walk could not continue past — also a full read. */\n | { kind: \"no-git\"; detail: string };\n\n/**\n * The last commit in the docs repo's history whose version of `rel` (a\n * docs-relative, `/`-separated path) has a body hashing to the stamped\n * `content_digest` — newest first, because the reading list wants the\n * SHORTEST honest diff, and any older commit holding the same bytes yields\n * the same one.\n */\nexport async function findVouchedAncestor(\n docsDir: string,\n rel: string,\n stampedDigest: string,\n): Promise<AncestorSearch> {\n // Belt to the callers' braces: `rel` is derived from loam's own path\n // spelling, never argv, but every spelled pathspec below rides an argv, so\n // an option-shaped or empty value is refused here rather than parsed by\n // git as a flag. Same reason base-git.ts refuses option-shaped refs.\n if (rel.trim() === \"\" || rel.startsWith(\"-\")) {\n return { kind: \"no-git\", detail: `'${rel}' is not a path git can be asked about.` };\n }\n // `--show-prefix` first, and not decoration (base-git.ts proved this): a\n // docs repo checked out as a subdirectory of a larger repository answers\n // `show` only under that prefix, and a walk whose paths all miss would\n // report \"no vouched ancestor\" about a history that holds one.\n const where = await gitPlain(docsDir, [\"rev-parse\", \"--show-prefix\"]);\n if (where.spawnError !== undefined || where.code !== 0) {\n return { kind: \"no-git\", detail: declineDetail(where) };\n }\n const prefix = where.out.trim();\n\n // The pathspec is cwd-relative (no prefix — `log` resolves it against the\n // cwd, unlike `show`'s repo-root spelling below). `-n` is the walk's bound.\n const log = await gitPlain(docsDir, [\n \"log\", \"-n\", String(ANCESTOR_WALK_CAP), \"--format=%H\", \"--\", rel,\n ]);\n if (log.spawnError !== undefined || log.code !== 0 || log.overflowed) {\n return { kind: \"no-git\", detail: declineDetail(log) };\n }\n // Only shas shaped like shas walk on — an unexpected line yields a shorter\n // walk, never a malformed argv (the gitq/moves.ts token-anchoring rule).\n const commits = log.out.split(\"\\n\").map((l) => l.trim()).filter((l) => /^[0-9a-f]{40}$/.test(l));\n // Sequential on purpose: newest-first order is the answer's definition, and\n // the walk stops at the first match — a fan-out would read history it does\n // not need and could answer with an older commit than the newest match.\n for (const commit of commits) {\n const shown = await gitPlain(docsDir, [\"show\", `${commit}:${prefix}${rel}`]);\n if (shown.spawnError !== undefined || shown.code === null) {\n // Git stopped answering (not installed mid-walk, killed, timed out, or\n // the output cap fired on a monstrous version). Abort rather than walk\n // on: each further spawn could wait out the whole timeout, and a \"none\"\n // built over commits nobody read would claim the history was searched.\n return {\n kind: \"no-git\",\n detail: shown.overflowed\n ? `the file at ${commit.slice(0, 12)} exceeds the git output cap.`\n : declineDetail(shown),\n };\n }\n // A real non-zero exit is git ANSWERING — the file is absent at this\n // commit (renamed since, or the commit only deleted it). Skip it.\n if (shown.code !== 0) continue;\n if (\n contentDigest(shown.out) === stampedDigest ||\n contentDigest(checkoutRendering(shown.out)) === stampedDigest\n ) {\n return { kind: \"found\", commit, text: shown.out };\n }\n }\n return {\n kind: \"none\",\n reason:\n commits.length < ANCESTOR_WALK_CAP\n ? \"body has no vouched ancestor in history\"\n : `body has no vouched ancestor in history — only the last ${ANCESTOR_WALK_CAP} commits touching the file were walked, and the vouched state may lie beyond them`,\n };\n}\n\nexport type BodyDiff =\n | { kind: \"ok\"; diff: string }\n /** No diff to print — the honest degrade is a full read, with this reason. */\n | { kind: \"failed\"; reason: string };\n\n/**\n * The file's diff from its last vouched state: `git diff <commit> -- <rel>`,\n * commit-to-working-tree, in the docs repo. The output is git's own — when a\n * hand edit touched the frontmatter after the vouch, its hunks ride along,\n * which is why the pack's prose says \"the file's diff\", never \"the body's\".\n */\nexport async function bodyDiffText(docsDir: string, commit: string, rel: string): Promise<BodyDiff> {\n // The commit came out of `findVouchedAncestor`'s own regex-vetted walk, but\n // this function is exported API: re-vet, so no future caller can place an\n // option-shaped string in the one argv slot that is not behind `--`.\n if (!/^[0-9a-f]{40}$/.test(commit)) {\n return { kind: \"failed\", reason: `'${commit}' is not a commit this diff can be asked about.` };\n }\n if (rel.trim() === \"\" || rel.startsWith(\"-\")) {\n return { kind: \"failed\", reason: `'${rel}' is not a path git can be asked about.` };\n }\n // `--no-optional-locks`: a commit-to-worktree diff may opportunistically\n // refresh `.git/index`'s stat cache, which is a WRITE — small, benign, and\n // exactly what a command documented as \"writes nothing\" must not do. The\n // global flag tells git to skip any operation that would take a lock it\n // does not strictly need. `--no-ext-diff --no-textconv` close the rest of\n // the config surface: `diff.external` / a `.gitattributes` diff driver make\n // `git diff` EXECUTE a user-configured program instead of printing a diff —\n // an arbitrary write hazard on a read-only path, and output no machine\n // consumer of `body.diff` could parse. The pack's diff is a machine\n // artifact, not a terminal rendering; git's own plumbing flags say so.\n const answer = await gitPlain(docsDir, [\n \"--no-optional-locks\", \"diff\", \"--no-ext-diff\", \"--no-textconv\", commit, \"--\", rel,\n ]);\n if (answer.spawnError !== undefined || answer.code !== 0 || answer.overflowed) {\n return { kind: \"failed\", reason: declineDetail(answer) };\n }\n if (answer.out.length > MAX_DIFF_TEXT_CHARS) {\n return { kind: \"failed\", reason: \"the body diff exceeds the pack's print cap — read the file\" };\n }\n return { kind: \"ok\", diff: answer.out };\n}\n"]}
|