@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,84 @@
|
|
|
1
|
+
# Verification record for FEAT-120 — written by `loam verify FEAT-120` (--results / --contract-results / --record).
|
|
2
|
+
#
|
|
3
|
+
# Every claim below was derived mechanically from this feature's own artifacts:
|
|
4
|
+
# delta.likec4, specs/<svc>/spec.md, specs/<svc>/arch.spec.md and specs/<svc>/
|
|
5
|
+
# openapi.yaml. Each verdict names who answered it: `answered_by: runner` means a
|
|
6
|
+
# test report's digest-tagged scenarios answered it mechanically — cucumber JSON,
|
|
7
|
+
# or loam's own {"loamScenarioReport": 1, ...} shape that any runner adapts into;
|
|
8
|
+
# `answered_by: external-runner` means a contract-test report's operationId-matched
|
|
9
|
+
# entries did, and `contractReport:` records which file a --contract-results run
|
|
10
|
+
# read; `answered_by: agent` means somebody's word about the code, which loam did
|
|
11
|
+
# not check. Nothing gates on any of them.
|
|
12
|
+
#
|
|
13
|
+
# A `scenario.tested` claim confirmed by an agent is ATTESTED, not run: loam
|
|
14
|
+
# reports it as `verify.scenario-attested` and the feature does not count as
|
|
15
|
+
# verified until a report answers it. `report:` records the file a --results run
|
|
16
|
+
# read — its sha256 and mtime say WHICH file, not that it came from that commit;
|
|
17
|
+
# no digest can say that.
|
|
18
|
+
#
|
|
19
|
+
# `checklist` is a digest of the claim ids. If `loam verify` stops reporting the same
|
|
20
|
+
# one, the feature changed after this was recorded and these answers are stale.
|
|
21
|
+
feature: FEAT-120
|
|
22
|
+
recorded: 2026-08-29
|
|
23
|
+
checklist: 060a0cc49fcb1d64
|
|
24
|
+
summary:
|
|
25
|
+
claims: 7
|
|
26
|
+
confirmed: 7
|
|
27
|
+
unconfirmed: 0
|
|
28
|
+
claims:
|
|
29
|
+
- id: event.declares-5ba57275
|
|
30
|
+
kind: event.declares
|
|
31
|
+
claim: notification-service declares it receives message 'payment.PaymentRefunded'
|
|
32
|
+
verdict: confirmed
|
|
33
|
+
answered_by: agent
|
|
34
|
+
evidence:
|
|
35
|
+
- internal/consumer/refund.go:18
|
|
36
|
+
note: the consumer subscribes to payment.events.v1 and dispatches PaymentRefunded to the refund notice renderer
|
|
37
|
+
- id: event.declares-02a85dfd
|
|
38
|
+
kind: event.declares
|
|
39
|
+
claim: payment-service declares it sends message 'payment.PaymentRefunded'
|
|
40
|
+
verdict: confirmed
|
|
41
|
+
answered_by: agent
|
|
42
|
+
evidence:
|
|
43
|
+
- src/main/java/com/marketplace/payments/outbox/RefundOutboxRelay.java:44
|
|
44
|
+
note: the outbox relay publishes payment.PaymentRefunded on the acquirer reconciliation callback
|
|
45
|
+
- id: scenario.tested-6a899c70
|
|
46
|
+
kind: scenario.tested
|
|
47
|
+
claim: scenario 'A completed refund is announced once' of requirement 'Notify a customer when a refund completes' (notification-service) is covered by a test
|
|
48
|
+
verdict: confirmed
|
|
49
|
+
answered_by: runner
|
|
50
|
+
evidence:
|
|
51
|
+
- "docs/features/FEAT-120-refund-notification/scenario-report.json: TestRefundNotice_SendsOnce"
|
|
52
|
+
- id: scenario.tested-173ff6e0
|
|
53
|
+
kind: scenario.tested
|
|
54
|
+
claim: scenario 'The broker redelivers a refund already announced' of requirement 'Notify a customer when a refund completes' (notification-service) is covered by a test
|
|
55
|
+
verdict: confirmed
|
|
56
|
+
answered_by: runner
|
|
57
|
+
evidence:
|
|
58
|
+
- "docs/features/FEAT-120-refund-notification/scenario-report.json: TestRefundNotice_IgnoresRedelivery"
|
|
59
|
+
- id: scenario.tested-b8dc0f33
|
|
60
|
+
kind: scenario.tested
|
|
61
|
+
claim: scenario 'Refund a captured payment in full' of requirement 'Refund a captured payment' (payment-service) is covered by a test
|
|
62
|
+
verdict: confirmed
|
|
63
|
+
answered_by: runner
|
|
64
|
+
evidence:
|
|
65
|
+
- "docs/features/FEAT-120-refund-notification/scenario-report.json: RefundPaymentTest#refundsInFull"
|
|
66
|
+
- id: scenario.tested-75b9e98d
|
|
67
|
+
kind: scenario.tested
|
|
68
|
+
claim: scenario 'A second refund of the same payment is refused' of requirement 'Refund a captured payment' (payment-service) is covered by a test
|
|
69
|
+
verdict: confirmed
|
|
70
|
+
answered_by: runner
|
|
71
|
+
evidence:
|
|
72
|
+
- "docs/features/FEAT-120-refund-notification/scenario-report.json: RefundPaymentTest#refusesSecondRefund"
|
|
73
|
+
- id: scenario.tested-8aa6f501
|
|
74
|
+
kind: scenario.tested
|
|
75
|
+
claim: scenario 'The refund is announced only after the acquirer reconciles' of requirement 'Refund a captured payment' (payment-service) is covered by a test
|
|
76
|
+
verdict: confirmed
|
|
77
|
+
answered_by: runner
|
|
78
|
+
evidence:
|
|
79
|
+
- "docs/features/FEAT-120-refund-notification/scenario-report.json: RefundOutboxTest#publishesOnceAfterReconciliation"
|
|
80
|
+
report:
|
|
81
|
+
path: docs/features/FEAT-120-refund-notification/scenario-report.json
|
|
82
|
+
digest: 602bd905c7a4878195fccb98f2134b6d883c0ef42c33b5d8772aaee067dfa3f5
|
|
83
|
+
mtime: 2026-08-28T18:49:56.422Z
|
|
84
|
+
scenarios: 5
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Order
|
|
2
|
+
|
|
3
|
+
What a customer has committed to buy: a set of items, a total they were shown,
|
|
4
|
+
and the intent to pay it. An order exists from the moment the customer confirms
|
|
5
|
+
— before any money moves, and whether or not the
|
|
6
|
+
[authorization](payments/authorization.md) succeeds.
|
|
7
|
+
|
|
8
|
+
An order is not a cart. A cart is what the customer is still editing; an order
|
|
9
|
+
is what they have decided. Nothing in the fleet edits an order in place.
|
|
10
|
+
|
|
11
|
+
The word is used in `capabilities/checkout/spec.md` and
|
|
12
|
+
`capabilities/order-notifications/spec.md`, and the services that carry parts of
|
|
13
|
+
those promises use it in the same sense. That agreement is what a glossary is
|
|
14
|
+
for, and the links to this file are what make it checkable: `glossary.unlinked`
|
|
15
|
+
would name this term the day nothing cited it any more.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Authorization
|
|
2
|
+
|
|
3
|
+
The card issuer's promise that the money for an [order](../order.md) is there
|
|
4
|
+
and reserved. It is not a payment: no funds have moved, and an authorization
|
|
5
|
+
expires if nothing captures it.
|
|
6
|
+
|
|
7
|
+
The distinction matters to the business and not only to the payments team. A
|
|
8
|
+
customer whose card was authorized has not been charged, so "we took your money"
|
|
9
|
+
is the wrong thing to tell them — and a confirmed order that was never
|
|
10
|
+
authorized is the failure `checkout#CHECKOUT-CHARGE-ONCE` exists to rule out.
|
|
11
|
+
|
|
12
|
+
Filed under `glossary/payments/` because the tree spells nesting the way the
|
|
13
|
+
capability tree does: this term's id is `payments/authorization`, and a flat
|
|
14
|
+
directory is not the only shape a growing vocabulary is allowed.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Per-service C4 for a UI service. A UI is a first-class C4 system: it has
|
|
2
|
+
// containers, it has runtime dependencies, and its pages (ui/pages/*.page.yaml)
|
|
3
|
+
// hang off the operations the edges below name.
|
|
4
|
+
//
|
|
5
|
+
// Every .likec4 file is parsed ON ITS OWN — loam never concatenates them — so
|
|
6
|
+
// this document declares the kinds it uses even though architecture/landscape.likec4
|
|
7
|
+
// declares the same ones. Leaving them out here does not inherit, it fails to parse
|
|
8
|
+
// (`c4.invalid`, an error), and the spec/API joins that read the model go quiet with it.
|
|
9
|
+
specification {
|
|
10
|
+
element softwareSystem
|
|
11
|
+
element container
|
|
12
|
+
element person
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
model {
|
|
16
|
+
// The actor belongs in a UI's own model in a way it does not in a backend's:
|
|
17
|
+
// the pages exist for this person, and the flows in ui/pages/ start at them.
|
|
18
|
+
// The landscape draws the same customer -> checkout-web edge at fleet scale;
|
|
19
|
+
// a person is exempt from the service/directory cross-check either way.
|
|
20
|
+
customer = person 'Customer'
|
|
21
|
+
|
|
22
|
+
checkoutWeb = softwareSystem 'checkout-web' {
|
|
23
|
+
description 'Customer-facing checkout UI'
|
|
24
|
+
|
|
25
|
+
spa = container 'spa' {
|
|
26
|
+
description 'Checkout SPA — cart, payment and order status pages'
|
|
27
|
+
technology 'React'
|
|
28
|
+
}
|
|
29
|
+
// A backend-for-frontend, drawn because it is where the fleet call actually
|
|
30
|
+
// originates: the browser holds a session cookie, the fleet speaks bearer
|
|
31
|
+
// tokens, and something has to be the place that translation happens. Drawn
|
|
32
|
+
// as a container of checkout-web and not as a service of its own, because it
|
|
33
|
+
// is deployed and released with the SPA and has no consumer but the SPA —
|
|
34
|
+
// a services/checkout-bff/ would owe a spec, a contract and an owner nobody has.
|
|
35
|
+
bff = container 'bff' {
|
|
36
|
+
description 'Backend-for-frontend: session to bearer, response shaping'
|
|
37
|
+
technology 'Node.js'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
customer -> spa 'Buys'
|
|
41
|
+
spa -> bff 'Calls the checkout BFF'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// The services this one actually calls. A service model carries everything the
|
|
45
|
+
// service touches, cross-boundary included, drawn with the edge the service
|
|
46
|
+
// really has — the landscape carries only the boundary crossing (checkout-web ->
|
|
47
|
+
// order-service), while the edge below says WHICH container makes the call.
|
|
48
|
+
// Without these three edges this file would be two boxes and a hop, and
|
|
49
|
+
// `c4.no-relationships` would be right to say a model that reaches nothing is
|
|
50
|
+
// almost never true.
|
|
51
|
+
orderService = softwareSystem 'order-service' {
|
|
52
|
+
description 'Owns the order lifecycle'
|
|
53
|
+
// Bind rather than rely on the title. A renamed box silently stops matching
|
|
54
|
+
// its directory everywhere the two are joined — in the landscape that costs
|
|
55
|
+
// `landscape.binding-unknown` (error) when the id is wrong, and here it is
|
|
56
|
+
// what `health.dependency-unmodelled` resolves a declared dependency
|
|
57
|
+
// through in the services that have a health.yaml. checkout-web has none:
|
|
58
|
+
// a UI is nobody's runtime dependency, so it declares no SLIs, no alerts
|
|
59
|
+
// and no dependency graph, and owes no arch.spec.md to cover them.
|
|
60
|
+
metadata {
|
|
61
|
+
service 'order-service'
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
paymentService = softwareSystem 'payment-service' {
|
|
65
|
+
description 'Owns payment authorization/capture'
|
|
66
|
+
metadata {
|
|
67
|
+
service 'payment-service'
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// `metadata { op }` is the C4 half of the operation join: the exact
|
|
72
|
+
// operationId order-service's openapi.yaml declares. What `loam validate`
|
|
73
|
+
// grades is the LANDSCAPE's twin of each edge below — `spine.op-undefined`
|
|
74
|
+
// (error) is what it says there when the id names no operation the callee
|
|
75
|
+
// defines. Carrying the op here anyway is what keeps the two documents from
|
|
76
|
+
// drifting into different answers about which operation this container calls;
|
|
77
|
+
// nothing would catch it if they did. Two calls to one service are two edges,
|
|
78
|
+
// not one edge with a list — a view has to be able to show that the status
|
|
79
|
+
// page and the checkout page stand on different operations.
|
|
80
|
+
checkoutWeb.bff -> orderService 'Calls createOrder' {
|
|
81
|
+
metadata {
|
|
82
|
+
op 'createOrder'
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
checkoutWeb.bff -> orderService 'Calls getOrder' {
|
|
86
|
+
metadata {
|
|
87
|
+
op 'getOrder'
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Card details go from the browser straight to payment-service and never
|
|
91
|
+
// through the BFF or order-service, which is why this edge starts at the SPA.
|
|
92
|
+
// It carries no `metadata { op }` deliberately: its twin in the landscape is
|
|
93
|
+
// the example's demonstration of `spine.op-link-missing` (warn) — the finding
|
|
94
|
+
// for an edge that SAYS it calls something and names no operation, so nothing
|
|
95
|
+
// can check the call still exists. Free here and one warning there, which is
|
|
96
|
+
// the asymmetry worth knowing: the fleet map is what the spine reads.
|
|
97
|
+
checkoutWeb.spa -> paymentService 'Calls the payment API'
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Views are the LikeC4 renderer's, never loam's — no check reads this block.
|
|
101
|
+
// Scoped to the system so the page answers "what does the checkout UI stand on"
|
|
102
|
+
// rather than redrawing the fleet, which architecture/landscape.likec4 already does.
|
|
103
|
+
views {
|
|
104
|
+
view checkout_web of checkoutWeb {
|
|
105
|
+
title 'checkout-web — containers and the services they call'
|
|
106
|
+
include *
|
|
107
|
+
include orderService, paymentService
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
service: checkout-web
|
|
3
|
+
status: draft
|
|
4
|
+
owner: checkout-team
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# checkout-web
|
|
8
|
+
|
|
9
|
+
The customer-facing checkout SPA. It owns no store and exposes no operations of its own —
|
|
10
|
+
every requirement below is satisfied by calling `order-service` and `payment-service` — which
|
|
11
|
+
is why no requirement here carries an `Operations:` line and why this directory holds no
|
|
12
|
+
`openapi.yaml`. That absence is graded on evidence, not on the file list: `service.no-openapi`
|
|
13
|
+
stays silent because the landscape parses and no op-linked edge points AT checkout-web, so
|
|
14
|
+
nobody is expecting a contract here. A UI in a fleet map that could not be read would get the
|
|
15
|
+
warning instead, because "I could not look" and "nobody calls me" are different facts.
|
|
16
|
+
|
|
17
|
+
**This spec names no `sources:`, on purpose.** `loam validate` reports `sources.absent`
|
|
18
|
+
against it, and that warning is the point: the UI baseline is not yet tied to code, so nothing
|
|
19
|
+
can tell you when the pages drift from what the SPA actually renders, and `loam vouch` would
|
|
20
|
+
refuse to stamp it. Every other service in this example names the paths in its own repository
|
|
21
|
+
its spec was written from; this one shows what the absence costs — a document that is internally
|
|
22
|
+
consistent, passes every join below, and is answerable to nothing outside itself.
|
|
23
|
+
|
|
24
|
+
## Requirements
|
|
25
|
+
|
|
26
|
+
### Requirement: Checkout flow
|
|
27
|
+
|
|
28
|
+
Requirement-ID: WEB-CHECKOUT
|
|
29
|
+
The UI SHALL carry a customer from a filled cart to a confirmed order in one pass, and SHALL
|
|
30
|
+
show a confirmation only for an order `order-service` has acknowledged — never for one the
|
|
31
|
+
client assumed went through.
|
|
32
|
+
|
|
33
|
+
Capability: checkout
|
|
34
|
+
Realizes: checkout#CHECKOUT-CHARGE-ONCE
|
|
35
|
+
|
|
36
|
+
#### Scenario: Complete a checkout
|
|
37
|
+
- **Given** a cart holding at least one line and a payment method the customer has entered
|
|
38
|
+
- **When** the customer confirms the purchase
|
|
39
|
+
- **Then** `createOrder` records the order and `authorizePayment` reserves the funds against it
|
|
40
|
+
- **And** the confirmation shows the order id the API returned, not one the UI minted
|
|
41
|
+
|
|
42
|
+
#### Scenario: Authorization is refused
|
|
43
|
+
- **Given** a filled cart and a card the issuer declines
|
|
44
|
+
- **When** the customer confirms the purchase
|
|
45
|
+
- **Then** the payment step stays open with the cart, address and delivery choice intact
|
|
46
|
+
- **And** the customer is asked for another payment method rather than being returned to the cart
|
|
47
|
+
|
|
48
|
+
#### Scenario: The customer submits twice
|
|
49
|
+
- **Given** a checkout that has already sent `createOrder` under an idempotency key
|
|
50
|
+
- **When** the customer presses the confirm button again before the first response arrives
|
|
51
|
+
- **Then** the second request carries the same key and `order-service` answers 409
|
|
52
|
+
- **And** the UI opens the order that already exists instead of starting a second checkout
|
|
53
|
+
|
|
54
|
+
### Requirement: Show order status
|
|
55
|
+
|
|
56
|
+
Requirement-ID: WEB-ORDER-STATUS
|
|
57
|
+
The UI SHALL show the current state of an order from `order-service`, and SHALL answer
|
|
58
|
+
identically for an order that does not exist and for one that belongs to somebody else, so a
|
|
59
|
+
guessed id cannot confirm that an order is real.
|
|
60
|
+
|
|
61
|
+
Capability: checkout
|
|
62
|
+
|
|
63
|
+
#### Scenario: Show the current state of an order
|
|
64
|
+
- **Given** a placed order the signed-in customer owns
|
|
65
|
+
- **When** the customer opens its status page
|
|
66
|
+
- **Then** the page renders the state `getOrder` returns
|
|
67
|
+
- **And** it does not fall back to the state the checkout flow last saw, stale since capture
|
|
68
|
+
|
|
69
|
+
#### Scenario: The order id is not the customer's
|
|
70
|
+
- **Given** an order id belonging to another customer
|
|
71
|
+
- **When** the signed-in customer opens its status page
|
|
72
|
+
- **Then** `order-service` answers 404 and the page says the order was not found
|
|
73
|
+
- **And** an id that was never issued produces the same page, so the difference leaks nothing
|
|
74
|
+
|
|
75
|
+
## UI
|
|
76
|
+
|
|
77
|
+
Pages live under `ui/pages/`, one file per page. Each page-spec joins the two axes a UI sits
|
|
78
|
+
between: `consumes:` names the fleet operations the page depends on, by the same `operationId`
|
|
79
|
+
the provider's `openapi.yaml` declares, and `behavior:` names the scenarios above that govern
|
|
80
|
+
what the page does with them. Where the matching landscape edge carries `metadata { op }`, the
|
|
81
|
+
fleet map and the page-spec name that operation twice and can be read against each other; where
|
|
82
|
+
it does not — the `checkout-web` → `payment-service` edge — the page-spec is the only place the
|
|
83
|
+
operation is written down at all. Planned: `loam render` generates an HTML prototype from the
|
|
84
|
+
page-spec, so the prototype and the requirement can never disagree about which endpoint a
|
|
85
|
+
button calls.
|
|
86
|
+
|
|
87
|
+
`split-payment.page.yaml` is the exception in this directory: it carries `feature: FEAT-101`
|
|
88
|
+
because the requirement governing it lives under `features/FEAT-101-payment-splitting/` and not
|
|
89
|
+
in this file yet. Its `behavior:` references are spelled with the feature id in front for exactly
|
|
90
|
+
that reason, and drop it once `loam archive` has folded the delta into the requirements above —
|
|
91
|
+
by hand, since no command reads page-specs yet and none will rewrite this one for you.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Page-spec (source of truth). planned: `loam render` generates an HTML prototype from this (model A).
|
|
2
|
+
# Links the architecture (consumes endpoints) and behavior (governing scenarios).
|
|
3
|
+
# No command reads page-specs yet — the format is illustrative, not contract.
|
|
4
|
+
page: checkout
|
|
5
|
+
title: Checkout
|
|
6
|
+
service: checkout-web
|
|
7
|
+
status: draft
|
|
8
|
+
layout:
|
|
9
|
+
- { type: heading, text: "Checkout" }
|
|
10
|
+
- { type: list, id: cart-lines, itemFields: [sku, quantity, amount] }
|
|
11
|
+
- { type: text, id: total, label: "Total" }
|
|
12
|
+
- { type: field, id: payment-method, label: "Card", required: true }
|
|
13
|
+
- { type: button, id: confirm, text: "Pay and place order" }
|
|
14
|
+
# Every branch a governing scenario describes needs somewhere on the page to
|
|
15
|
+
# happen. This one is where "Authorization is refused" lands: without it the
|
|
16
|
+
# scenario is satisfied by an alert box nobody designed.
|
|
17
|
+
- { type: banner, id: declined, text: "That card was declined — try another payment method" }
|
|
18
|
+
consumes:
|
|
19
|
+
# `consumes:` names the FLEET operation the page depends on — the exact
|
|
20
|
+
# operationId the provider's openapi.yaml declares — not the hop the call
|
|
21
|
+
# takes to get there. The two hops differ: `createOrder` goes through the
|
|
22
|
+
# `bff` container in model.likec4, `authorizePayment` leaves the browser
|
|
23
|
+
# directly so card details never touch our servers. Naming the hop here would
|
|
24
|
+
# make one dependency look unlike the other when the thing that can actually
|
|
25
|
+
# break under them is the same: the provider's contract.
|
|
26
|
+
- { service: order-service, op: createOrder } # POST /orders
|
|
27
|
+
# The one dependency here whose edge carries no `metadata { op }` — see
|
|
28
|
+
# model.likec4, and `spine.op-link-missing` on its landscape twin. Nothing
|
|
29
|
+
# joins this line to anything, so it is a claim rather than a check: that is
|
|
30
|
+
# what the missing link costs, written at the only place that still knows.
|
|
31
|
+
- { service: payment-service, op: authorizePayment } # POST /payments
|
|
32
|
+
behavior:
|
|
33
|
+
# "<service> / <requirement> / <scenario>" for a LIVING scenario: the three
|
|
34
|
+
# names that locate it in services/<service>/spec.md. A scenario that still
|
|
35
|
+
# lives in a feature carries the feature id in front instead (see
|
|
36
|
+
# split-payment.page.yaml) — until FEAT-101 archives, there is no living
|
|
37
|
+
# requirement to point at.
|
|
38
|
+
- "checkout-web / Checkout flow / Complete a checkout"
|
|
39
|
+
- "checkout-web / Checkout flow / Authorization is refused"
|
|
40
|
+
- "checkout-web / Checkout flow / The customer submits twice"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Page-spec (source of truth). planned: `loam render` generates an HTML prototype from this (model A).
|
|
2
|
+
# Links the architecture (consumes endpoints) and behavior (governing scenarios).
|
|
3
|
+
# No command reads page-specs yet — the format is illustrative, not contract.
|
|
4
|
+
page: order-status
|
|
5
|
+
title: Order status
|
|
6
|
+
service: checkout-web
|
|
7
|
+
status: draft
|
|
8
|
+
layout:
|
|
9
|
+
- { type: heading, text: "Order {orderId}" }
|
|
10
|
+
- { type: text, id: state, label: "Status" }
|
|
11
|
+
- { type: list, id: order-lines, itemFields: [sku, quantity, amount] }
|
|
12
|
+
# One empty state for both "no such order" and "not yours" — the page cannot
|
|
13
|
+
# tell them apart, and the governing scenario says it must not try: two
|
|
14
|
+
# different messages would turn the id into an oracle for whether an order exists.
|
|
15
|
+
- { type: banner, id: not-found, text: "We could not find that order" }
|
|
16
|
+
consumes:
|
|
17
|
+
# One operation, one page. The status page reads and never writes, which is
|
|
18
|
+
# why `cancelOrder` is absent: it is order-service's own requirement, gated on
|
|
19
|
+
# `user/orders:cancel`, and a page that consumed it would owe that permission
|
|
20
|
+
# a place in the layout and a scenario naming the refusal.
|
|
21
|
+
- { service: order-service, op: getOrder } # GET /orders/{id}
|
|
22
|
+
behavior:
|
|
23
|
+
- "checkout-web / Show order status / Show the current state of an order"
|
|
24
|
+
- "checkout-web / Show order status / The order id is not the customer's"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Page-spec (source of truth). planned: `loam render` generates an HTML prototype from this (model A).
|
|
2
|
+
# Links the architecture (consumes endpoints) and behavior (governing scenarios).
|
|
3
|
+
# No command reads page-specs yet — the format is illustrative, not contract.
|
|
4
|
+
page: split-payment
|
|
5
|
+
title: Split payment
|
|
6
|
+
service: checkout-web
|
|
7
|
+
feature: FEAT-101
|
|
8
|
+
status: draft
|
|
9
|
+
layout:
|
|
10
|
+
- { type: heading, text: "Split this payment" }
|
|
11
|
+
- { type: list, id: payees, itemFields: [name, amount] }
|
|
12
|
+
- { type: button, id: add-payee, text: "Add payee" }
|
|
13
|
+
# Where "A split that does not sum to the total cannot be confirmed" lands. A
|
|
14
|
+
# disabled confirm button with no remainder on screen is a dead end rather
|
|
15
|
+
# than an explanation, and the scenario says the 10.00 is shown.
|
|
16
|
+
- { type: text, id: unassigned, label: "Unassigned" }
|
|
17
|
+
- { type: button, id: confirm, text: "Confirm split" }
|
|
18
|
+
consumes:
|
|
19
|
+
# createSplit only: the 201 carries back the split this page shows, so nothing
|
|
20
|
+
# here re-reads it through `getSplit`.
|
|
21
|
+
- { service: payment-split-service, op: createSplit } # POST /splits
|
|
22
|
+
behavior:
|
|
23
|
+
# BOTH scenarios of FEAT-101's ADDED `Split payment on checkout` — a page that
|
|
24
|
+
# names only the happy one leaves its own refusal path governed by nothing.
|
|
25
|
+
- "FEAT-101 / checkout-web / Split payment on checkout / Customer splits a payment across two payees"
|
|
26
|
+
- "FEAT-101 / checkout-web / Split payment on checkout / A split that does not sum to the total cannot be confirmed"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: ADR-order-service-0001
|
|
3
|
+
status: accepted
|
|
4
|
+
date: 2026-04-22
|
|
5
|
+
service: order-service
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# 1. Orchestrate the payment from order-service rather than choreograph it
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
Placing an order needs an authorization, and the two obvious shapes were both on the table.
|
|
12
|
+
|
|
13
|
+
**Choreography**: order-service publishes `OrderPlaced`, payment-service consumes it,
|
|
14
|
+
authorizes, and publishes `PaymentAuthorized`; every service reacts to events and nobody is in
|
|
15
|
+
charge. **Orchestration**: order-service calls `authorizePayment` synchronously as part of
|
|
16
|
+
`createOrder` and drives the lifecycle itself, consuming `PaymentAuthorized` only to learn how
|
|
17
|
+
the decision landed.
|
|
18
|
+
|
|
19
|
+
The deciding question was not coupling — both shapes couple, one through a topic and one
|
|
20
|
+
through a contract — but where a placement that half-happened becomes somebody's problem. In
|
|
21
|
+
the choreographed shape there is no moment at which any single service can answer "is this
|
|
22
|
+
order going to be paid for", so the answer lives in the gaps between four consumers, and the
|
|
23
|
+
customer-facing failure (an order acknowledged that no card will ever be charged for) is
|
|
24
|
+
detectable only by reconciliation after the fact. checkout-web also needs a synchronous answer:
|
|
25
|
+
a checkout page that acknowledges and then silently drops the order is the failure this fleet
|
|
26
|
+
gets support tickets about.
|
|
27
|
+
|
|
28
|
+
## Decision
|
|
29
|
+
order-service orchestrates. `createOrder` calls `authorizePayment` before it acknowledges,
|
|
30
|
+
holds the order in `awaiting_payment`, and advances it to `confirmed` when
|
|
31
|
+
`payment.PaymentAuthorized` arrives. The event is a completion signal for a call this service
|
|
32
|
+
already made — not the trigger that starts the work.
|
|
33
|
+
|
|
34
|
+
Concretely: the synchronous edge `order-service -> payment-service` carries
|
|
35
|
+
`metadata { op 'authorizePayment' }`, and the asynchronous one carries
|
|
36
|
+
`metadata { consumes 'payment.PaymentAuthorized' }`. Both are drawn, because both are real.
|
|
37
|
+
|
|
38
|
+
## Consequences
|
|
39
|
+
- order-service is a hard runtime dependency on payment-service for placement, and
|
|
40
|
+
`health.yaml` says so (`criticality: critical`). A payment-service outage stops placements;
|
|
41
|
+
it is meant to be visible as `order_placement_failures`, not absorbed.
|
|
42
|
+
- The lifecycle is readable in one file — the state machine in `domain` — instead of inferred
|
|
43
|
+
from four services' consumers. This is what makes ORD-CANCEL expressible at all: something
|
|
44
|
+
has to own the transition that refuses a cancellation.
|
|
45
|
+
- Every call in the placement path must be idempotent, because orchestration means retrying.
|
|
46
|
+
ARCH-ORD-IDEMPOTENCY carries that obligation, and it is the reason `createOrder` takes an
|
|
47
|
+
`Idempotency-Key` header at all.
|
|
48
|
+
- Redelivery is still ours to handle: the authorization arrives at-least-once, so
|
|
49
|
+
ARCH-ORD-DEDUPE keeps an applied-payment ledger. Orchestration removed the coordination
|
|
50
|
+
problem, not the delivery guarantee.
|
|
51
|
+
- The v1 endpoint predates this decision and does not participate in it — it places orders
|
|
52
|
+
without an idempotency key, which is exactly why FEAT-112 retires it rather than fixing it.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
service: order-service
|
|
3
|
+
status: draft
|
|
4
|
+
owner: orders-team
|
|
5
|
+
sources:
|
|
6
|
+
- src/main/kotlin/
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# order-service — architecture spec
|
|
10
|
+
|
|
11
|
+
The **living architecture spec**: the obligations no business scenario in `spec.md` was ever
|
|
12
|
+
going to carry. "Place an order" says an order is placed; it does not say that the placement
|
|
13
|
+
and its `OrderPlaced` row commit together, that a caller's retry must not buy the basket twice,
|
|
14
|
+
or that a redelivered `PaymentAuthorized` must confirm the order exactly once. Those are the
|
|
15
|
+
corners an agent writing the implementation cuts first, so they are written down mechanically
|
|
16
|
+
here instead of being trusted to a reviewer.
|
|
17
|
+
|
|
18
|
+
Same grammar as `spec.md`, and a separate requirement namespace — an arch requirement never
|
|
19
|
+
collides with a business one of the same name, and `loam gherkin` emits these tagged
|
|
20
|
+
`@architecture` so the suite knows an integration or operational test is being asked for.
|
|
21
|
+
|
|
22
|
+
What makes the file checkable is the `Covers:` line: it names the model objects a requirement's
|
|
23
|
+
scenarios exercise, and `loam validate` grades it both ways. Every entry must resolve against
|
|
24
|
+
this service's own `model.likec4` plus the landscape (`covers.unknown`, with a did-you-mean
|
|
25
|
+
hint, is the typo guard — a mistyped entry otherwise costs exactly the coverage it was written
|
|
26
|
+
for), and every `slis[].name` and `alerts[].name` in `health.yaml` must be named by one of them
|
|
27
|
+
(`health.uncovered`, or a signal ships to the on-call rotation with nothing testing it). Edge
|
|
28
|
+
entries below are spelled as this model draws them — `orderService.outbox -> kafka.orderEvents`
|
|
29
|
+
— because the endpoint that matters is the container; `order-service -> payment-service`
|
|
30
|
+
resolves too, through the `metadata { service }` binding on the root element.
|
|
31
|
+
|
|
32
|
+
## Requirements
|
|
33
|
+
|
|
34
|
+
### Requirement: Order creation is idempotent under a caller's key
|
|
35
|
+
|
|
36
|
+
Requirement-ID: ARCH-ORD-IDEMPOTENCY
|
|
37
|
+
The service SHALL record the `Idempotency-Key` of every accepted `createOrder` in the same
|
|
38
|
+
transaction that stores the order, and SHALL replay the stored response for a repeat of that
|
|
39
|
+
key rather than placing a second order or requesting a second authorization. checkout-web's
|
|
40
|
+
retry on a gateway timeout is not a rare path — it is the normal shape of a mobile checkout —
|
|
41
|
+
and without the key a customer's flaky connection charges the card twice.
|
|
42
|
+
|
|
43
|
+
Covers: orderService.db, orderService.domain -> paymentService
|
|
44
|
+
|
|
45
|
+
#### Scenario: checkout-web retries a placement it never saw acknowledged
|
|
46
|
+
- **Given** an order placed under `Idempotency-Key` "ik-8842" with an authorization already requested
|
|
47
|
+
- **When** the identical `createOrder` request arrives again after the caller timed out
|
|
48
|
+
- **Then** the stored response for "ik-8842" is returned unchanged
|
|
49
|
+
- **And** payment-service receives no second `authorizePayment` call
|
|
50
|
+
|
|
51
|
+
#### Scenario: An authorization call that times out is retried, not abandoned
|
|
52
|
+
- **Given** a `createOrder` whose `authorizePayment` call times out with no response
|
|
53
|
+
- **When** the placement path retries the authorization
|
|
54
|
+
- **Then** `authorizePayment` is called again for the same `orderId`, amount and currency
|
|
55
|
+
- **And** payment-service's idempotency by order id returns the first authorization instead of reserving funds twice
|
|
56
|
+
|
|
57
|
+
### Requirement: OrderPlaced leaves through the transactional outbox
|
|
58
|
+
|
|
59
|
+
Requirement-ID: ARCH-ORD-OUTBOX
|
|
60
|
+
The service SHALL write the order and its `OrderPlaced` event into the same database
|
|
61
|
+
transaction and SHALL publish the event from the outbox relay — never by writing to the
|
|
62
|
+
database and then to kafka. A dual write has no failure mode in which both sides agree: a crash
|
|
63
|
+
between them either announces an order nobody stored or stores one nobody hears about, and
|
|
64
|
+
notification-service is the service that pays for the second case.
|
|
65
|
+
|
|
66
|
+
Covers: orderService.outbox, orderService.outbox -> kafka.orderEvents
|
|
67
|
+
|
|
68
|
+
#### Scenario: The broker is unavailable when an order is placed
|
|
69
|
+
- **Given** an accepted order whose `OrderPlaced` row is still in the outbox table
|
|
70
|
+
- **When** kafka is unreachable
|
|
71
|
+
- **Then** the order stays committed and is readable through `getOrder`
|
|
72
|
+
- **And** the relay publishes the event once kafka returns, without the caller retrying
|
|
73
|
+
|
|
74
|
+
#### Scenario: The relay crashes after publishing and before marking the row sent
|
|
75
|
+
- **Given** an outbox row published to `order.events.v1` but not yet marked sent
|
|
76
|
+
- **When** the relay restarts and picks the row up again
|
|
77
|
+
- **Then** the event is published a second time with the same order id
|
|
78
|
+
- **And** consumers deduplicate on it, because this relay promises at-least-once and not exactly-once
|
|
79
|
+
|
|
80
|
+
### Requirement: PaymentAuthorized is consumed at least once and applied once
|
|
81
|
+
|
|
82
|
+
Requirement-ID: ARCH-ORD-DEDUPE
|
|
83
|
+
The service SHALL record the `paymentId` of every `payment.PaymentAuthorized` it applies, in
|
|
84
|
+
the transaction that advances the order, and SHALL ignore a message whose `paymentId` is
|
|
85
|
+
already recorded. The producer's outbox promises at-least-once delivery and a rebalance
|
|
86
|
+
redelivers whatever was in flight, so a consumer without a ledger is a consumer that confirms
|
|
87
|
+
an order it already confirmed — and, once ORD-CANCEL has moved on, resurrects a cancelled one.
|
|
88
|
+
|
|
89
|
+
Covers: orderService.consumer, kafka.paymentEvents -> orderService.consumer
|
|
90
|
+
Consumes: payment.PaymentAuthorized
|
|
91
|
+
|
|
92
|
+
#### Scenario: The same authorization is redelivered after a consumer rebalance
|
|
93
|
+
- **Given** an order confirmed from `payment.PaymentAuthorized` for payment "pay-77"
|
|
94
|
+
- **When** the same message is redelivered after the consumer group rebalances
|
|
95
|
+
- **Then** the ledger already holds "pay-77" and the message is acknowledged without a write
|
|
96
|
+
- **And** the order's state and its updated-at timestamp are unchanged
|
|
97
|
+
|
|
98
|
+
#### Scenario: The consumer fails mid-batch and the offset is not committed
|
|
99
|
+
- **Given** a batch of authorizations where the third message fails to apply
|
|
100
|
+
- **When** the consumer restarts from the last committed offset
|
|
101
|
+
- **Then** the first two are recognized by the ledger and skipped
|
|
102
|
+
- **And** the third is applied, so no authorization is lost to a partial batch
|
|
103
|
+
|
|
104
|
+
### Requirement: Order placement is measured and paged
|
|
105
|
+
|
|
106
|
+
Requirement-ID: ARCH-ORD-SIGNALS
|
|
107
|
+
The service SHALL export availability and placement-latency SLIs, and SHALL page the orders
|
|
108
|
+
on-call when placements fail at a sustained rate. Placement is the fleet's revenue path and it
|
|
109
|
+
fails silently from the customer's side — a broken authorization call looks like a slow
|
|
110
|
+
checkout — so the alert is on the outcome, not on any one dependency being down.
|
|
111
|
+
|
|
112
|
+
Covers: sli:availability, sli:order_placement_latency_p95_ms, alert:order_placement_failures
|
|
113
|
+
|
|
114
|
+
#### Scenario: Sustained placement failures page the on-call
|
|
115
|
+
- **Given** more than 2% of `createOrder` calls failing over five minutes
|
|
116
|
+
- **When** the alert rules are evaluated
|
|
117
|
+
- **Then** `order_placement_failures` pages the orders on-call with the runbook link
|
|
118
|
+
|
|
119
|
+
#### Scenario: The SLIs are exported against their objectives
|
|
120
|
+
- **Given** the service is serving traffic
|
|
121
|
+
- **When** its metrics endpoint is scraped
|
|
122
|
+
- **Then** `availability` and `order_placement_latency_p95_ms` report values against their SLOs
|
|
123
|
+
|
|
124
|
+
#### Scenario: A degraded payment-service shows up as placement latency, not as a silent stall
|
|
125
|
+
- **Given** payment-service answering `authorizePayment` slowly but successfully
|
|
126
|
+
- **When** placements queue behind it
|
|
127
|
+
- **Then** `order_placement_latency_p95_ms` rises above its objective before any request times out
|