@starci/skills 1.1.0
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/INDEX.md +74 -0
- package/INDEX.vi.md +75 -0
- package/README.md +44 -0
- package/README.vi.md +43 -0
- package/SKILL.md +135 -0
- package/SKILL.vi.md +128 -0
- package/alias/INDEX.md +104 -0
- package/alias/INDEX.vi.md +104 -0
- package/alias/alias.json +297 -0
- package/bin/starci-skills.mjs +219 -0
- package/knowledge/INDEX.md +22 -0
- package/knowledge/INDEX.vi.md +22 -0
- package/knowledge/grammars/starci/DNA.md +146 -0
- package/knowledge/grammars/starci/DNA.vi.md +146 -0
- package/knowledge/grammars/starci/INDEX.md +25 -0
- package/knowledge/grammars/starci/INDEX.vi.md +25 -0
- package/knowledge/grammars/starci/family.md +50 -0
- package/knowledge/grammars/starci/family.vi.md +50 -0
- package/knowledge/grammars/starci/idioms.md +120 -0
- package/knowledge/grammars/starci/idioms.vi.md +121 -0
- package/knowledge/grammars/starci/playbook.md +36 -0
- package/knowledge/grammars/starci/playbook.vi.md +35 -0
- package/knowledge/patterns/be/INDEX.md +29 -0
- package/knowledge/patterns/be/INDEX.vi.md +29 -0
- package/knowledge/patterns/be/comment.md +80 -0
- package/knowledge/patterns/be/comment.vi.md +80 -0
- package/knowledge/patterns/be/error.md +86 -0
- package/knowledge/patterns/be/error.vi.md +86 -0
- package/knowledge/patterns/be/folder.md +87 -0
- package/knowledge/patterns/be/folder.vi.md +86 -0
- package/knowledge/patterns/be/function.md +80 -0
- package/knowledge/patterns/be/function.vi.md +80 -0
- package/knowledge/patterns/be/imports.md +79 -0
- package/knowledge/patterns/be/imports.vi.md +79 -0
- package/knowledge/patterns/be/naming.md +87 -0
- package/knowledge/patterns/be/naming.vi.md +87 -0
- package/knowledge/patterns/be/test.md +79 -0
- package/knowledge/patterns/be/test.vi.md +79 -0
- package/knowledge/patterns/be/typing.md +73 -0
- package/knowledge/patterns/be/typing.vi.md +73 -0
- package/knowledge/patterns/fe/INDEX.md +29 -0
- package/knowledge/patterns/fe/INDEX.vi.md +29 -0
- package/knowledge/patterns/fe/comment.md +70 -0
- package/knowledge/patterns/fe/comment.vi.md +70 -0
- package/knowledge/patterns/fe/error.md +61 -0
- package/knowledge/patterns/fe/error.vi.md +61 -0
- package/knowledge/patterns/fe/folder.md +98 -0
- package/knowledge/patterns/fe/folder.vi.md +95 -0
- package/knowledge/patterns/fe/function.md +70 -0
- package/knowledge/patterns/fe/function.vi.md +70 -0
- package/knowledge/patterns/fe/imports.md +87 -0
- package/knowledge/patterns/fe/imports.vi.md +87 -0
- package/knowledge/patterns/fe/naming.md +77 -0
- package/knowledge/patterns/fe/naming.vi.md +77 -0
- package/knowledge/patterns/fe/test.md +73 -0
- package/knowledge/patterns/fe/test.vi.md +73 -0
- package/knowledge/patterns/fe/typing.md +67 -0
- package/knowledge/patterns/fe/typing.vi.md +67 -0
- package/knowledge/ui/INDEX.md +101 -0
- package/knowledge/ui/INDEX.vi.md +101 -0
- package/knowledge/ui/composition/INDEX.md +60 -0
- package/knowledge/ui/composition/INDEX.vi.md +63 -0
- package/knowledge/ui/composition/accent.md +73 -0
- package/knowledge/ui/composition/accent.vi.md +74 -0
- package/knowledge/ui/composition/action.md +55 -0
- package/knowledge/ui/composition/action.vi.md +56 -0
- package/knowledge/ui/composition/coverage.md +37 -0
- package/knowledge/ui/composition/coverage.vi.md +37 -0
- package/knowledge/ui/composition/cta.md +79 -0
- package/knowledge/ui/composition/cta.vi.md +79 -0
- package/knowledge/ui/composition/feedback.md +55 -0
- package/knowledge/ui/composition/feedback.vi.md +56 -0
- package/knowledge/ui/composition/hierarchy.md +83 -0
- package/knowledge/ui/composition/hierarchy.vi.md +82 -0
- package/knowledge/ui/composition/layout.md +91 -0
- package/knowledge/ui/composition/layout.vi.md +93 -0
- package/knowledge/ui/composition/responsive.md +67 -0
- package/knowledge/ui/composition/responsive.vi.md +67 -0
- package/knowledge/ui/composition/state.md +105 -0
- package/knowledge/ui/composition/state.vi.md +106 -0
- package/knowledge/ui/presentation/INDEX.md +94 -0
- package/knowledge/ui/presentation/INDEX.vi.md +69 -0
- package/knowledge/ui/presentation/boundary.md +170 -0
- package/knowledge/ui/presentation/boundary.vi.md +169 -0
- package/knowledge/ui/presentation/font.md +155 -0
- package/knowledge/ui/presentation/font.vi.md +156 -0
- package/knowledge/ui/presentation/gap.md +181 -0
- package/knowledge/ui/presentation/gap.vi.md +181 -0
- package/knowledge/ui/presentation/margin.md +168 -0
- package/knowledge/ui/presentation/margin.vi.md +171 -0
- package/knowledge/ui/presentation/measure.md +174 -0
- package/knowledge/ui/presentation/measure.vi.md +178 -0
- package/knowledge/ui/presentation/overflow.md +142 -0
- package/knowledge/ui/presentation/overflow.vi.md +142 -0
- package/knowledge/ui/presentation/padding.md +293 -0
- package/knowledge/ui/presentation/padding.vi.md +292 -0
- package/knowledge/ui/presentation/surface.md +166 -0
- package/knowledge/ui/presentation/surface.vi.md +166 -0
- package/knowledge/ui/presentation/text-flow.md +123 -0
- package/knowledge/ui/presentation/text-flow.vi.md +123 -0
- package/knowledge/ui/presentation/tone.md +114 -0
- package/knowledge/ui/presentation/tone.vi.md +113 -0
- package/knowledge/ui/proof/INDEX.md +55 -0
- package/knowledge/ui/proof/INDEX.vi.md +55 -0
- package/knowledge/ui/proof/accessibility.md +67 -0
- package/knowledge/ui/proof/accessibility.vi.md +68 -0
- package/knowledge/ui/proof/contrast.md +58 -0
- package/knowledge/ui/proof/contrast.vi.md +57 -0
- package/knowledge/ui/proof/focus.md +76 -0
- package/knowledge/ui/proof/focus.vi.md +76 -0
- package/knowledge/ui/proof/motion.md +63 -0
- package/knowledge/ui/proof/motion.vi.md +64 -0
- package/knowledge/ui/proof/render-truth.md +63 -0
- package/knowledge/ui/proof/render-truth.vi.md +63 -0
- package/operators/INDEX.md +199 -0
- package/operators/INDEX.vi.md +199 -0
- package/operators/architecture-decide/errors.json +115 -0
- package/operators/architecture-decide/operator.json +20 -0
- package/operators/architecture-decide/operator.md +133 -0
- package/operators/architecture-decide/operator.vi.md +130 -0
- package/operators/architecture-decide/self-test.mjs +221 -0
- package/operators/architecture-decide/validate.mjs +112 -0
- package/operators/backend-source-apply/errors.json +66 -0
- package/operators/backend-source-apply/operator.json +20 -0
- package/operators/backend-source-apply/operator.md +189 -0
- package/operators/backend-source-apply/operator.vi.md +185 -0
- package/operators/backend-source-apply/self-test.mjs +257 -0
- package/operators/backend-source-apply/validate.mjs +240 -0
- package/operators/business-decide/errors.json +78 -0
- package/operators/business-decide/operator.json +20 -0
- package/operators/business-decide/operator.md +176 -0
- package/operators/business-decide/operator.vi.md +174 -0
- package/operators/business-decide/self-test.mjs +225 -0
- package/operators/business-decide/validate.mjs +277 -0
- package/operators/content-generate/errors.json +106 -0
- package/operators/content-generate/operator.json +21 -0
- package/operators/content-generate/operator.md +155 -0
- package/operators/content-generate/operator.vi.md +155 -0
- package/operators/content-generate/self-test.mjs +288 -0
- package/operators/content-generate/validate.mjs +196 -0
- package/operators/errors.json +178 -0
- package/operators/frontend-direction-decide/errors.json +135 -0
- package/operators/frontend-direction-decide/operator.json +21 -0
- package/operators/frontend-direction-decide/operator.md +167 -0
- package/operators/frontend-direction-decide/operator.vi.md +167 -0
- package/operators/frontend-direction-decide/self-test.mjs +226 -0
- package/operators/frontend-direction-decide/validate.mjs +146 -0
- package/operators/frontend-presentation-resolve/errors.json +42 -0
- package/operators/frontend-presentation-resolve/operator.json +19 -0
- package/operators/frontend-presentation-resolve/operator.md +142 -0
- package/operators/frontend-presentation-resolve/operator.vi.md +140 -0
- package/operators/frontend-presentation-resolve/self-test.mjs +163 -0
- package/operators/frontend-presentation-resolve/validate.mjs +143 -0
- package/operators/frontend-source-apply/errors.json +30 -0
- package/operators/frontend-source-apply/operator.json +20 -0
- package/operators/frontend-source-apply/operator.md +127 -0
- package/operators/frontend-source-apply/operator.vi.md +126 -0
- package/operators/frontend-source-apply/self-test.mjs +214 -0
- package/operators/frontend-source-apply/validate.mjs +133 -0
- package/operators/frontend-surface-audit/errors.json +5 -0
- package/operators/frontend-surface-audit/operator.json +22 -0
- package/operators/frontend-surface-audit/operator.md +121 -0
- package/operators/frontend-surface-audit/operator.vi.md +121 -0
- package/operators/frontend-surface-audit/self-test.mjs +185 -0
- package/operators/frontend-surface-audit/validate.mjs +127 -0
- package/operators/git-publish/errors.json +54 -0
- package/operators/git-publish/operator.json +20 -0
- package/operators/git-publish/operator.md +180 -0
- package/operators/git-publish/operator.vi.md +178 -0
- package/operators/git-publish/self-test.mjs +177 -0
- package/operators/git-publish/validate.mjs +106 -0
- package/operators/platform-operate/errors.json +90 -0
- package/operators/platform-operate/operator.json +22 -0
- package/operators/platform-operate/operator.md +162 -0
- package/operators/platform-operate/operator.vi.md +160 -0
- package/operators/platform-operate/self-test.mjs +202 -0
- package/operators/platform-operate/validate.mjs +193 -0
- package/operators/quality-verify/errors.json +54 -0
- package/operators/quality-verify/operator.json +20 -0
- package/operators/quality-verify/operator.md +185 -0
- package/operators/quality-verify/operator.vi.md +177 -0
- package/operators/quality-verify/self-test.mjs +210 -0
- package/operators/quality-verify/validate.mjs +197 -0
- package/operators/release-deploy/errors.json +158 -0
- package/operators/release-deploy/operator.json +23 -0
- package/operators/release-deploy/operator.md +183 -0
- package/operators/release-deploy/operator.vi.md +181 -0
- package/operators/release-deploy/self-test.mjs +235 -0
- package/operators/release-deploy/validate.mjs +129 -0
- package/operators/uat-verify/errors.json +78 -0
- package/operators/uat-verify/operator.json +25 -0
- package/operators/uat-verify/operator.md +162 -0
- package/operators/uat-verify/operator.vi.md +161 -0
- package/operators/uat-verify/self-test.mjs +270 -0
- package/operators/uat-verify/validate.mjs +202 -0
- package/operators/workspace-bind/errors.json +90 -0
- package/operators/workspace-bind/operator.json +21 -0
- package/operators/workspace-bind/operator.md +148 -0
- package/operators/workspace-bind/operator.vi.md +148 -0
- package/operators/workspace-bind/self-test.mjs +205 -0
- package/operators/workspace-bind/validate.mjs +152 -0
- package/package.json +50 -0
- package/readiness/initialization/workspaces/commit-policy.json +91 -0
- package/readiness/initialization/workspaces/config.schema.json +35 -0
- package/readiness/initialization/workspaces/device-state.schema.json +54 -0
- package/readiness/initialization/workspaces/local-route.schema.json +206 -0
- package/readiness/initialization/workspaces/portable-route.schema.json +200 -0
- package/resources/INDEX.md +96 -0
- package/resources/INDEX.vi.md +99 -0
- package/resources/agents/profiles/claude.json +131 -0
- package/resources/agents/profiles/openai.json +131 -0
- package/resources/orchestrator.json +71 -0
- package/resources/tools.json +85 -0
- package/routing.json +272 -0
- package/scripts/alias-registry.mjs +31 -0
- package/scripts/device-state.mjs +497 -0
- package/scripts/device-state.spec.mjs +18 -0
- package/scripts/errors-registry.mjs +68 -0
- package/scripts/generate-alias-doc.mjs +66 -0
- package/scripts/generate-grammar-dna.mjs +387 -0
- package/scripts/generate-operators-index.mjs +95 -0
- package/scripts/generate-presentation-owned.mjs +681 -0
- package/scripts/install-cli.spec.mjs +74 -0
- package/scripts/json-schema.mjs +94 -0
- package/scripts/operator-md.mjs +96 -0
- package/scripts/run-operator-self-tests.mjs +36 -0
- package/scripts/validate-alias.mjs +165 -0
- package/scripts/validate-defaults.mjs +72 -0
- package/scripts/validate-knowledge-citations.mjs +90 -0
- package/scripts/validate-operator.mjs +125 -0
- package/scripts/validate-request.mjs +80 -0
- package/scripts/validate-resources.mjs +117 -0
- package/scripts/validate-response.mjs +141 -0
- package/scripts/validate-routing.mjs +91 -0
- package/scripts/validate-step.mjs +50 -0
- package/scripts/validate-templates.mjs +226 -0
- package/scripts/validate-templates.spec.mjs +144 -0
- package/scripts/validate-workflows.mjs +106 -0
- package/scripts/workspace-portable.mjs +389 -0
- package/scripts/workspace-portable.spec.mjs +246 -0
- package/templates/README.md +47 -0
- package/templates/README.vi.md +45 -0
- package/templates/changes.example.md +27 -0
- package/templates/grammars.template.md +34 -0
- package/templates/kinds/architecture-decision.contract.json +14 -0
- package/templates/kinds/architecture-decision.skeleton.md +52 -0
- package/templates/kinds/backend-source-application.contract.json +10 -0
- package/templates/kinds/backend-source-application.skeleton.md +34 -0
- package/templates/kinds/business-promise-authority.contract.json +13 -0
- package/templates/kinds/business-promise-authority.skeleton.md +67 -0
- package/templates/kinds/capture.schema.json +33 -0
- package/templates/kinds/changes.contract.json +10 -0
- package/templates/kinds/changes.skeleton.md +26 -0
- package/templates/kinds/checks.schema.json +44 -0
- package/templates/kinds/claims.schema.json +165 -0
- package/templates/kinds/conformance.schema.json +15 -0
- package/templates/kinds/content-brief.contract.json +12 -0
- package/templates/kinds/content-brief.skeleton.md +45 -0
- package/templates/kinds/content-generation-receipt.contract.json +11 -0
- package/templates/kinds/content-generation-receipt.skeleton.md +40 -0
- package/templates/kinds/content-review.contract.json +11 -0
- package/templates/kinds/content-review.skeleton.md +45 -0
- package/templates/kinds/contract.schema.json +28 -0
- package/templates/kinds/coverage-matrix.schema.json +86 -0
- package/templates/kinds/coverage.schema.json +62 -0
- package/templates/kinds/current-state.schema.json +40 -0
- package/templates/kinds/delta.schema.json +95 -0
- package/templates/kinds/e2e.schema.json +63 -0
- package/templates/kinds/frontend-direction-decision.contract.json +93 -0
- package/templates/kinds/frontend-direction-decision.skeleton.md +68 -0
- package/templates/kinds/frontend-presentation-resolution.contract.json +11 -0
- package/templates/kinds/frontend-presentation-resolution.skeleton.md +32 -0
- package/templates/kinds/frontend-source-application.contract.json +10 -0
- package/templates/kinds/frontend-source-application.skeleton.md +30 -0
- package/templates/kinds/frontend-surface-audit.contract.json +52 -0
- package/templates/kinds/frontend-surface-audit.skeleton.md +32 -0
- package/templates/kinds/gate-result.schema.json +64 -0
- package/templates/kinds/git-publication.contract.json +82 -0
- package/templates/kinds/git-publication.skeleton.md +61 -0
- package/templates/kinds/independent-critique.contract.json +9 -0
- package/templates/kinds/independent-critique.skeleton.md +28 -0
- package/templates/kinds/inventory.schema.json +35 -0
- package/templates/kinds/model.schema.json +99 -0
- package/templates/kinds/mutations.schema.json +65 -0
- package/templates/kinds/platform-operation-receipt.contract.json +76 -0
- package/templates/kinds/platform-operation-receipt.skeleton.md +54 -0
- package/templates/kinds/probes.schema.json +130 -0
- package/templates/kinds/proof.schema.json +17 -0
- package/templates/kinds/quality-verification.contract.json +87 -0
- package/templates/kinds/quality-verification.skeleton.md +58 -0
- package/templates/kinds/release-deployment.contract.json +85 -0
- package/templates/kinds/release-deployment.skeleton.md +67 -0
- package/templates/kinds/route.schema.json +293 -0
- package/templates/kinds/stack-model.schema.json +90 -0
- package/templates/kinds/uat-capture.schema.json +35 -0
- package/templates/kinds/uat-flow-verification.contract.json +11 -0
- package/templates/kinds/uat-flow-verification.skeleton.md +47 -0
- package/templates/kinds/uat-snapshot.schema.json +96 -0
- package/templates/kinds/uat-verdicts.schema.json +41 -0
- package/templates/kinds/ui-coverage.schema.json +77 -0
- package/templates/kinds/verdicts.schema.json +39 -0
- package/templates/kinds/workspace-route-binding.contract.json +12 -0
- package/templates/kinds/workspace-route-binding.skeleton.md +60 -0
- package/templates/kinds/writes.schema.json +37 -0
- package/templates/operator.template.md +73 -0
- package/templates/patterns.template.md +31 -0
- package/templates/step/request.schema.json +42 -0
- package/templates/step/response.schema.json +144 -0
- package/templates/ui-composition.template.md +36 -0
- package/templates/ui-presentation.template.md +57 -0
- package/templates/ui-proof.template.md +34 -0
- package/workflows/README.md +37 -0
- package/workflows/README.vi.md +37 -0
- package/workflows/backend-feature.json +59 -0
- package/workflows/content-unit.json +19 -0
- package/workflows/frontend-new-surface.json +81 -0
- package/workflows/frontend-reconstruct.json +67 -0
- package/workflows/frontend-refine.json +67 -0
- package/workflows/frontend-with-uat.json +78 -0
- package/workflows/full-feature.json +104 -0
- package/workflows/release.json +29 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# business.decide
|
|
2
|
+
|
|
3
|
+
## Job
|
|
4
|
+
|
|
5
|
+
Decide and publish one evidence-backed business promise as durable backend-owned authority, frozen
|
|
6
|
+
behind a complete promise-to-enforcement coverage matrix, or reconcile that published head against
|
|
7
|
+
the source that was actually delivered.
|
|
8
|
+
|
|
9
|
+
## Two modes, one head
|
|
10
|
+
|
|
11
|
+
`mode` decides which half of this operator runs. Under `model` the promise is modelled and the
|
|
12
|
+
coverage matrix is frozen: steps 4, 5 and 6 run and the branch writes
|
|
13
|
+
`response/data/coverage-matrix.json`. Under `reconcile` nothing is modelled again; step 7 compares the
|
|
14
|
+
head that was already published against the source a backend run delivered, and the Input
|
|
15
|
+
`backend-source-application` is required in that mode, because a reconciliation with no delivered source
|
|
16
|
+
is an opinion about code nobody read. The Inputs table marks it optional because the requirement is
|
|
17
|
+
conditional, and `validate.mjs` refuses a `reconcile` branch whose request does not bind it. Both
|
|
18
|
+
modes end at the same place: one head under `@worktrees/businesses/<featureId>` and one
|
|
19
|
+
`response/data/model.json` that says exactly what that head now holds.
|
|
20
|
+
|
|
21
|
+
## A first run starts from the person's promise
|
|
22
|
+
|
|
23
|
+
A feature that no source implements yet has no fact claim by construction. On a first run under mode
|
|
24
|
+
model, the promise the person stated in `promise` is recorded as the one intent claim, bound to
|
|
25
|
+
`request/request.json#requirements.promise` instead of a source line, and the model is built from it;
|
|
26
|
+
`EVIDENCE_MISSING` applies to a fact claim without a file behind it, never to the intent of a promise
|
|
27
|
+
that is being decided for the first time. Every enforcing row of the coverage matrix still rests on a
|
|
28
|
+
fact claim, so a greenfield head publishes with its enforcing rows open, which is what `pending` means.
|
|
29
|
+
|
|
30
|
+
## Separate the claim before modelling it
|
|
31
|
+
|
|
32
|
+
Modelling begins by separating every observation into fact, intent, example, unknown, or
|
|
33
|
+
contradiction, and that separation is written to `response/data/claims.json` before anything is
|
|
34
|
+
modelled from it. The separation exists to make one substitution impossible: an example, a
|
|
35
|
+
screenshot, or an owner's intent illustrates a promise, and only an observed fact in routed source
|
|
36
|
+
proves that the promise is enforced. A coverage row that asserts enforcement therefore cites at least
|
|
37
|
+
one fact claim, and every fact claim binds the observed source head. A claim that cites a source
|
|
38
|
+
nobody bound is invalid input rather than a warning, because an unbound citation cannot be told apart
|
|
39
|
+
from an invented one. Two claims that disagree about the same behaviour are never averaged into a
|
|
40
|
+
middle reading; the contradiction stops the invocation and returns to its owner.
|
|
41
|
+
|
|
42
|
+
## No promise without coverage
|
|
43
|
+
|
|
44
|
+
A promise is publishable only when every dimension the request declared carries exactly one
|
|
45
|
+
disposition, and every consumer and lifecycle branch the operator discovered is disposed by name.
|
|
46
|
+
`dimensions` defaults to the dimensions of the previous head, which is why it is required on a first
|
|
47
|
+
run: the first publication is the one that decides what the promise is even accountable for. That
|
|
48
|
+
rule exists because "full access" was modelled, implemented, and published while its course,
|
|
49
|
+
community, blog, AI, mock-interview, legacy-sale, quota, settlement, renewal, cancellation, and
|
|
50
|
+
recovery consumers had never all been proved. The promise was true in the offer and false at the
|
|
51
|
+
guard. Four prohibitions carry the repair, and each is enforced rather than advised. A discovered
|
|
52
|
+
consumer with no matrix row is `CONSUMER_UNPROVEN`. A mandatory dimension, meaning actor and
|
|
53
|
+
eligibility, offer entry, read entry, purchase side effect, settlement, idempotency, entitlement
|
|
54
|
+
consumer, and denial, can never be marked not applicable when it was declared. A dimension whose
|
|
55
|
+
branch was observed in source can never be marked not applicable either, because it was found, so it
|
|
56
|
+
applies. A preserve or replace disposition without a negative proof is rejected, since positive proof
|
|
57
|
+
alone shows the promise is granted and never that it is denied when it should be.
|
|
58
|
+
|
|
59
|
+
## The dispositions and what each one must carry
|
|
60
|
+
|
|
61
|
+
`preserve` keeps the existing enforcement and carries owner, source, positive proof, negative proof,
|
|
62
|
+
and one fact claim. `replace` changes the enforcement and carries the same substance for the new
|
|
63
|
+
path. `retire` closes a path on purpose and carries owner, source, proof the path is closed, and one
|
|
64
|
+
fact claim. `defer` postpones a branch to a named owner, carries a deferral reference, and carries no
|
|
65
|
+
proof at all, because proof for work that has not happened is the most convincing kind of false pass.
|
|
66
|
+
`not-applicable` states the branch cannot occur and carries nothing: no owner, no source, no proof, no
|
|
67
|
+
consumer, no claim. `defer` is a disposition, so a deferred branch does not block publication; what
|
|
68
|
+
blocks publication is silence.
|
|
69
|
+
|
|
70
|
+
## One flat authority root
|
|
71
|
+
|
|
72
|
+
One feature owns exactly one head directory, `<businesses root>/features/<featureId>`, whose
|
|
73
|
+
`model.json` is the head, and step 8 takes an exclusive lease on that alias before it writes. Two
|
|
74
|
+
branches of the same step may not publish the same feature. `features/` is the only segment between
|
|
75
|
+
the root and a feature: a project segment inserted below the root starts a second authority tree that
|
|
76
|
+
later readers never find, so any head that is not exactly `features/<featureId>` is refused. The head
|
|
77
|
+
is classified absent, fresh, or stale against the frozen evidence and the frozen source head, and
|
|
78
|
+
that classification decides which lifecycle transition is legal. Rejection preserves lineage by
|
|
79
|
+
naming the previous head rather than erasing it, and `implemented` is never published on the strength
|
|
80
|
+
of a plan: delivered source is compared against the frozen matrix first, under `reconcile`.
|
|
81
|
+
|
|
82
|
+
## Boundary
|
|
83
|
+
|
|
84
|
+
Context is read-only apart from the one feature head. The operator writes only `response/` of its own
|
|
85
|
+
branch, `response.md`, `response/data/claims.json`, `response/data/coverage-matrix.json` under mode
|
|
86
|
+
model, `response/data/model.json` and `response.json`, plus the one feature head under
|
|
87
|
+
`@worktrees/businesses/<featureId>`. It never publishes a promise while a discovered consumer or
|
|
88
|
+
lifecycle branch carries no disposition, promotes an example, a screenshot, or an intent claim into
|
|
89
|
+
product truth, invents an actor, entitlement, quota, payment, settlement, or lifecycle behaviour the
|
|
90
|
+
evidence does not state, advances a head through a transition the lifecycle does not allow, or writes
|
|
91
|
+
a head below a project segment under the businesses root. It does not modify product source,
|
|
92
|
+
architecture authority, frontend authority, or backend implementation, and it never claims that an
|
|
93
|
+
implementation, a quality gate, or a UAT run has passed.
|
|
94
|
+
|
|
95
|
+
## Context
|
|
96
|
+
|
|
97
|
+
| Alias | Bind | Required |
|
|
98
|
+
| --- | --- | --- |
|
|
99
|
+
| `@worktrees/businesses/<featureId>` | the promise head and its lifecycle state, by content address from the registry; the one place this operator writes outside its branch | yes |
|
|
100
|
+
| `@workspaces/be` | the routed backend checkout read at the frozen head; every fact claim cites it by path, line range, and head | yes |
|
|
101
|
+
|
|
102
|
+
## Inputs
|
|
103
|
+
|
|
104
|
+
| Kind | From | Required |
|
|
105
|
+
| --- | --- | --- |
|
|
106
|
+
| `architecture-decision` | `architecture.decide`; architecture evidence, never a source of business behaviour | no |
|
|
107
|
+
| `backend-source-application` | `backend.source.apply`; the delivered source a reconciliation reads, required under mode `reconcile` and refused otherwise | no |
|
|
108
|
+
|
|
109
|
+
## Requirements
|
|
110
|
+
|
|
111
|
+
| Field | Type | Default | Ask |
|
|
112
|
+
| --- | --- | --- | --- |
|
|
113
|
+
| `featureId` | id | — | The one feature the promise belongs to |
|
|
114
|
+
| `mode` | choice | model | `model` decides and publishes the promise; `reconcile` compares the published head against delivered source |
|
|
115
|
+
| `promise` | prompt | the promise the previous head states | The promise in the person's words; required on a first run, when no head exists yet, and recorded as the intent claim bound to `request/request.json#requirements.promise` |
|
|
116
|
+
| `targetState` | choice | — | `pending`, `in-progress`, `implemented` or `rejected` for the published head |
|
|
117
|
+
| `dimensions` | list | the dimensions of the previous head | The coverage surface this promise is accountable for; required on a first run, because no previous head declares it |
|
|
118
|
+
| `approval` | id | null | The owner approval the transition needs, supplied on resume after `APPROVAL_REQUIRED` |
|
|
119
|
+
| `resume` | token | null | The blocked branch's token when re-entering after a stop |
|
|
120
|
+
|
|
121
|
+
## Steps
|
|
122
|
+
|
|
123
|
+
| # | Step | Params | Reads | Writes | Stops with |
|
|
124
|
+
| --- | --- | --- | --- | --- | --- |
|
|
125
|
+
| 1 | Validate the gate and resume | `resume`, `mode` | `request/request.json`, @workspaces/be at the frozen head | — | `INVALID_INPUT`, `SOURCE_DRIFT`, `NO_PROGRESS` |
|
|
126
|
+
| 2 | Normalize the evidence into claims | — | @workspaces/be, every observation with its role, path, line range and head, input `architecture-decision` when present as evidence only, @tools/git | `response/data/claims.json` | `EVIDENCE_MISSING`, `CONTRADICTION_UNRESOLVED` |
|
|
127
|
+
| 3 | Check the published head and the transition authority | `featureId`, `targetState`, `approval` | @worktrees/businesses/<featureId>: the current head, its state and its frozen evidence | — | `LIFECYCLE_TRANSITION_INVALID`, `AUTHORITY_CONFLICT`, `APPROVAL_REQUIRED` |
|
|
128
|
+
| 4 | Model the promise, its actor and its eligibility, under mode model: fact claims carry every enforcing row, the intent claim carries the promise itself | `promise` | `response/data/claims.json`, @workspaces/be at the frozen head, @tools/websearch | — | `EVIDENCE_MISSING` |
|
|
129
|
+
| 5 | Freeze the coverage matrix, under mode model | `dimensions` | `response/data/claims.json`, @workspaces/be and the surface it discovers | `response/data/coverage-matrix.json` | `COVERAGE_INCOMPLETE`, `CONSUMER_UNPROVEN` |
|
|
130
|
+
| 6 | Dispose legacy coexistence, under mode model | — | `response/data/coverage-matrix.json`: the legacy create, read and settle rows and their proof | — | `CONTRADICTION_UNRESOLVED` |
|
|
131
|
+
| 7 | Reconcile against delivered source, under mode reconcile | — | input `backend-source-application`, @workspaces/be at the frozen head, the coverage matrix frozen at the published head | — | `RECONCILIATION_DISCREPANCY` |
|
|
132
|
+
| 8 | Publish one head under an exclusive lease | — | `response/data/claims.json`, @worktrees/businesses/<featureId> at the previous head | @worktrees/businesses/<featureId> as the new model.json head, `response/data/model.json`, @tools/sourcewrite | `SOURCE_DRIFT` |
|
|
133
|
+
| 9 | Emit | — | everything above | `response/response.md`, `response/response.json` | — |
|
|
134
|
+
|
|
135
|
+
Legacy create, read, and settle each take their own row when they are declared: a new sale path may
|
|
136
|
+
retire legacy creation only while already-purchased rights stay readable and pending legacy
|
|
137
|
+
settlement still completes, and the retirement carries proof that the creation path is closed. The
|
|
138
|
+
matrix is content addressed and its fingerprint travels in the binding, so backend implementation,
|
|
139
|
+
quality integration, and UAT can prove they consumed the same matrix rather than a paraphrase of it.
|
|
140
|
+
A resume begins again at step 1, reuses only unchanged fingerprinted observations, and consumes the
|
|
141
|
+
exact delta; republished evidence arrives as a new evidence fingerprint, because the same fingerprint
|
|
142
|
+
cannot yield a different answer.
|
|
143
|
+
|
|
144
|
+
## Outputs
|
|
145
|
+
|
|
146
|
+
| Kind | File | Type | Required |
|
|
147
|
+
| --- | --- | --- | --- |
|
|
148
|
+
| `business-promise-authority` | `response/response.md` | md | yes |
|
|
149
|
+
| `claims` | `response/data/claims.json` | data | yes |
|
|
150
|
+
| `coverage-matrix` | `response/data/coverage-matrix.json` | data | no |
|
|
151
|
+
| `model` | `response/data/model.json` | data | yes |
|
|
152
|
+
|
|
153
|
+
## Stops
|
|
154
|
+
|
|
155
|
+
| Code | Disposition |
|
|
156
|
+
| --- | --- |
|
|
157
|
+
| `INVALID_INPUT` | terminate |
|
|
158
|
+
| `SOURCE_DRIFT` | terminate |
|
|
159
|
+
| `NO_PROGRESS` | terminate |
|
|
160
|
+
| `EVIDENCE_MISSING` | terminate |
|
|
161
|
+
| `CONTRADICTION_UNRESOLVED` | terminate |
|
|
162
|
+
| `LIFECYCLE_TRANSITION_INVALID` | terminate |
|
|
163
|
+
| `AUTHORITY_CONFLICT` | terminate |
|
|
164
|
+
| `APPROVAL_REQUIRED` | terminate |
|
|
165
|
+
| `COVERAGE_INCOMPLETE` | terminate |
|
|
166
|
+
| `CONSUMER_UNPROVEN` | terminate |
|
|
167
|
+
| `RECONCILIATION_DISCREPANCY` | terminate |
|
|
168
|
+
|
|
169
|
+
## Next
|
|
170
|
+
|
|
171
|
+
| When | Operator |
|
|
172
|
+
| --- | --- |
|
|
173
|
+
| the promise is published and a frontend surface must carry it | `frontend.direction.decide` |
|
|
174
|
+
| the promise is published and a backend contract must carry it | `backend.source.apply` |
|
|
175
|
+
| the promise needs boundaries and data ownership decided before it can be enforced | `architecture.decide` |
|
|
176
|
+
| the head is reconciled against the delivered source and the delivery may be published | `git.publish` |
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# business.decide
|
|
2
|
+
|
|
3
|
+
## Việc
|
|
4
|
+
|
|
5
|
+
Quyết và publish một lời hứa nghiệp vụ có bằng chứng thành thẩm quyền bền do backend sở hữu, đóng
|
|
6
|
+
băng sau một ma trận phủ đầy đủ từ lời hứa tới điểm thực thi, hoặc đối chiếu chính head đã publish ấy
|
|
7
|
+
với source thực sự đã được giao.
|
|
8
|
+
|
|
9
|
+
## Hai chế độ, một head
|
|
10
|
+
|
|
11
|
+
`mode` quyết định nửa nào của operator này chạy. Dưới `model`, lời hứa được mô hình hoá và ma trận
|
|
12
|
+
phủ được đóng băng: bước 4, 5 và 6 chạy, và nhánh ghi `response/data/coverage-matrix.json`. Dưới
|
|
13
|
+
`reconcile` không mô hình lại gì cả; bước 7 đối chiếu head đã publish với source mà một lượt backend
|
|
14
|
+
đã giao, và Input `backend-source-application` là bắt buộc trong chế độ ấy, vì một cuộc đối chiếu không
|
|
15
|
+
có source đã giao chỉ là ý kiến về đoạn code chưa ai đọc. Bảng Đầu vào ghi nó là tuỳ chọn vì tính bắt
|
|
16
|
+
buộc phụ thuộc chế độ, và `validate.mjs` từ chối một nhánh `reconcile` mà request không ràng nó. Hai
|
|
17
|
+
chế độ kết thúc ở cùng một chỗ: một head dưới `@worktrees/businesses/<featureId>` và một
|
|
18
|
+
`response/data/model.json` nói đúng những gì head ấy đang giữ.
|
|
19
|
+
|
|
20
|
+
## Lần chạy đầu bắt đầu từ lời hứa của người
|
|
21
|
+
|
|
22
|
+
Một feature chưa có source hiện thực thì theo định nghĩa không có claim fact. Ở lần chạy đầu dưới chế
|
|
23
|
+
độ model, lời hứa người dùng nêu trong `promise` được ghi thành claim intent duy nhất, ràng vào
|
|
24
|
+
`request/request.json#requirements.promise` thay vì một dòng source, và mô hình dựng từ đó;
|
|
25
|
+
`EVIDENCE_MISSING` áp cho claim fact không có file đứng sau, không bao giờ áp cho intent của một lời
|
|
26
|
+
hứa đang được quyết lần đầu. Mọi hàng enforcing của coverage matrix vẫn phải tựa trên claim fact, nên
|
|
27
|
+
head greenfield publish với các hàng enforcing còn mở, đó chính là nghĩa của `pending`.
|
|
28
|
+
|
|
29
|
+
## Tách loại claim trước khi mô hình hoá
|
|
30
|
+
|
|
31
|
+
Mô hình hoá bắt đầu bằng việc tách mọi quan sát thành fact, intent, example, unknown hay
|
|
32
|
+
contradiction, và cách tách ấy được ghi vào `response/data/claims.json` trước khi có gì được mô hình
|
|
33
|
+
từ nó. Nó tồn tại để một phép thay thế trở thành bất khả: một ví dụ, một ảnh chụp màn hình hay ý định
|
|
34
|
+
của người chủ chỉ minh hoạ lời hứa, còn chỉ một fact quan sát được trong source được route mới chứng
|
|
35
|
+
minh lời hứa đó có người thực thi. Vì vậy một hàng phủ khẳng định có thực thi phải dẫn ít nhất một
|
|
36
|
+
claim fact, và mọi claim fact phải ràng head source đã quan sát. Một claim dẫn nguồn không ai ràng là
|
|
37
|
+
input sai chứ không phải cảnh báo, vì một trích dẫn không ràng thì không phân biệt được với một trích
|
|
38
|
+
dẫn bịa. Hai claim mâu thuẫn về cùng một hành vi không bao giờ được lấy trung bình thành một cách đọc
|
|
39
|
+
ở giữa; mâu thuẫn dừng lượt chạy và trả về cho chủ của nó.
|
|
40
|
+
|
|
41
|
+
## Không có phủ thì không có lời hứa
|
|
42
|
+
|
|
43
|
+
Một lời hứa chỉ publish được khi mọi chiều mà request đã khai đều mang đúng một disposition, và mọi
|
|
44
|
+
consumer cùng nhánh vòng đời mà operator phát hiện đều được xử lý đích danh. `dimensions` mặc định
|
|
45
|
+
lấy theo các chiều của head trước, nên nó bắt buộc ở lần chạy đầu: lần publish đầu tiên chính là lần
|
|
46
|
+
quyết định lời hứa phải chịu trách nhiệm về những gì. Luật này có vì "full access" từng được mô hình,
|
|
47
|
+
cài đặt và publish trong khi các consumer khoá học, cộng đồng, blog, AI, phỏng vấn thử, bán legacy,
|
|
48
|
+
quota, tất toán, gia hạn, huỷ và khôi phục chưa từng được chứng minh đủ. Lời hứa đúng ở chỗ chào bán
|
|
49
|
+
và sai ở chỗ guard. Bốn điều cấm gánh phần sửa, và mỗi điều được cưỡng chế chứ không phải khuyên. Một
|
|
50
|
+
consumer đã phát hiện mà không có hàng trong ma trận là `CONSUMER_UNPROVEN`. Một chiều bắt buộc, gồm
|
|
51
|
+
actor và eligibility, offer entry, read entry, purchase side effect, settlement, idempotency,
|
|
52
|
+
entitlement consumer và denial, không bao giờ được đánh không áp dụng khi nó đã được khai. Một chiều
|
|
53
|
+
mà nhánh của nó đã được quan sát trong source cũng không bao giờ được đánh không áp dụng, vì đã tìm
|
|
54
|
+
thấy tức là có áp dụng. Một disposition preserve hay replace mà thiếu negative proof bị từ chối, vì
|
|
55
|
+
chứng minh dương chỉ cho thấy lời hứa được cấp, không bao giờ cho thấy nó bị từ chối đúng lúc phải từ
|
|
56
|
+
chối.
|
|
57
|
+
|
|
58
|
+
## Các disposition và phần thực chất mỗi loại phải mang
|
|
59
|
+
|
|
60
|
+
`preserve` giữ nguyên phần thực thi đang có và mang owner, source, positive proof, negative proof và
|
|
61
|
+
một claim fact. `replace` đổi phần thực thi và mang đúng ngần ấy thực chất cho đường mới. `retire`
|
|
62
|
+
đóng một đường có chủ ý và mang owner, source, bằng chứng đường đã đóng, và một claim fact. `defer`
|
|
63
|
+
hoãn một nhánh cho một chủ có tên, mang một tham chiếu hoãn, và không mang bằng chứng nào, vì bằng
|
|
64
|
+
chứng cho việc chưa làm là kiểu pass giả thuyết phục nhất. `not-applicable` nói nhánh không thể xảy
|
|
65
|
+
ra và không mang gì cả: không owner, không source, không proof, không consumer, không claim. `defer`
|
|
66
|
+
là một disposition nên nhánh được hoãn không chặn publish; thứ chặn publish là sự im lặng.
|
|
67
|
+
|
|
68
|
+
## Một gốc thẩm quyền phẳng
|
|
69
|
+
|
|
70
|
+
Một feature sở hữu đúng một thư mục head, `<gốc businesses>/features/<featureId>`, mà `model.json`
|
|
71
|
+
trong đó là head, và bước 8 giữ lease độc quyền trên alias ấy trước khi ghi. Hai nhánh của cùng một
|
|
72
|
+
bước không được publish cùng một feature. `features/` là đoạn duy nhất giữa gốc và một feature: chèn
|
|
73
|
+
một đoạn project dưới gốc là sinh ra cây thẩm quyền thứ hai mà người đọc sau không bao giờ tìm ra,
|
|
74
|
+
nên mọi head không đúng `features/<featureId>` đều bị từ chối. Head được phân loại absent, fresh hay
|
|
75
|
+
stale so với bằng chứng đã đóng băng và head source đã đóng băng, và phân loại đó quyết định chuyển
|
|
76
|
+
trạng thái nào hợp lệ. Từ chối vẫn giữ dòng dõi bằng cách nêu tên head trước thay vì xoá nó, và
|
|
77
|
+
`implemented` không bao giờ được publish dựa trên một kế hoạch: source đã giao phải được đối chiếu
|
|
78
|
+
với ma trận đóng băng trước, dưới `reconcile`.
|
|
79
|
+
|
|
80
|
+
## Ranh giới
|
|
81
|
+
|
|
82
|
+
Context chỉ đọc, trừ đúng một head feature. Operator chỉ ghi `response/` của nhánh mình, gồm
|
|
83
|
+
`response.md`, `response/data/claims.json`, `response/data/coverage-matrix.json` dưới chế độ model,
|
|
84
|
+
`response/data/model.json` và `response.json`, cùng một head feature dưới
|
|
85
|
+
`@worktrees/businesses/<featureId>`. Nó không bao giờ publish một lời hứa khi còn một consumer hay
|
|
86
|
+
nhánh vòng đời đã phát hiện chưa có disposition, không nâng một ví dụ, một ảnh chụp hay một claim ý
|
|
87
|
+
định thành sự thật sản phẩm, không bịa hành vi actor, entitlement, quota, thanh toán, tất toán hay
|
|
88
|
+
vòng đời mà bằng chứng không nói, không đẩy head qua một chuyển trạng thái vòng đời không cho phép,
|
|
89
|
+
và không ghi head dưới một đoạn project trong gốc businesses. Nó không sửa source sản phẩm, thẩm
|
|
90
|
+
quyền kiến trúc, thẩm quyền frontend hay phần cài đặt backend, và không bao giờ tuyên bố rằng một bản
|
|
91
|
+
cài đặt, một cổng chất lượng hay một lượt UAT đã qua.
|
|
92
|
+
|
|
93
|
+
## Context
|
|
94
|
+
|
|
95
|
+
| Alias | Bind | Bắt buộc |
|
|
96
|
+
| --- | --- | --- |
|
|
97
|
+
| `@worktrees/businesses/<featureId>` | head lời hứa và trạng thái vòng đời của nó, theo địa chỉ nội dung từ registry; nơi duy nhất operator này ghi ngoài nhánh của mình | có |
|
|
98
|
+
| `@workspaces/be` | checkout backend được route, đọc ở head đã đóng băng; mọi claim fact dẫn nó theo path, khoảng dòng và head | có |
|
|
99
|
+
|
|
100
|
+
## Đầu vào
|
|
101
|
+
|
|
102
|
+
| Kind | Từ đâu | Bắt buộc |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| `architecture-decision` | `architecture.decide`; bằng chứng kiến trúc, không bao giờ là nguồn của hành vi nghiệp vụ | không |
|
|
105
|
+
| `backend-source-application` | `backend.source.apply`; source đã giao mà một cuộc đối chiếu phải đọc, bắt buộc dưới chế độ `reconcile` và bị từ chối ngoài chế độ ấy | không |
|
|
106
|
+
|
|
107
|
+
## Yêu cầu
|
|
108
|
+
|
|
109
|
+
| Field | Kiểu | Mặc định | Hỏi |
|
|
110
|
+
| --- | --- | --- | --- |
|
|
111
|
+
| `featureId` | id | — | Một feature duy nhất mà lời hứa thuộc về |
|
|
112
|
+
| `mode` | choice | model | `model` quyết và publish lời hứa; `reconcile` đối chiếu head đã publish với source đã giao |
|
|
113
|
+
| `promise` | prompt | the promise the previous head states | Lời hứa bằng lời của người dùng; bắt buộc ở lần chạy đầu khi chưa có head, và được ghi thành claim intent ràng vào `request/request.json#requirements.promise` |
|
|
114
|
+
| `targetState` | choice | — | `pending`, `in-progress`, `implemented` hay `rejected` cho head được publish |
|
|
115
|
+
| `dimensions` | list | the dimensions of the previous head | Bề mặt phủ mà lời hứa này chịu trách nhiệm; bắt buộc ở lần chạy đầu, vì chưa head nào khai nó |
|
|
116
|
+
| `approval` | id | null | Phê duyệt của chủ mà chuyển trạng thái cần, nhập khi chạy lại sau `APPROVAL_REQUIRED` |
|
|
117
|
+
| `resume` | token | null | Token của nhánh bị chặn khi vào lại sau một mã dừng |
|
|
118
|
+
|
|
119
|
+
## Các bước
|
|
120
|
+
|
|
121
|
+
| # | Bước | Tham số | Đọc | Ghi | Dừng với |
|
|
122
|
+
| --- | --- | --- | --- | --- | --- |
|
|
123
|
+
| 1 | Kiểm gate và chạy lại | `resume`, `mode` | `request/request.json`, @workspaces/be ở head đóng băng | — | `INVALID_INPUT`, `SOURCE_DRIFT`, `NO_PROGRESS` |
|
|
124
|
+
| 2 | Chuẩn hoá bằng chứng thành claim | — | @workspaces/be, mọi quan sát kèm vai, path, khoảng dòng và head, đầu vào `architecture-decision` nếu có và chỉ như bằng chứng, @tools/git | `response/data/claims.json` | `EVIDENCE_MISSING`, `CONTRADICTION_UNRESOLVED` |
|
|
125
|
+
| 3 | Kiểm head đã publish và thẩm quyền chuyển trạng thái | `featureId`, `targetState`, `approval` | @worktrees/businesses/<featureId>: head hiện tại, trạng thái và bằng chứng đóng băng của nó | — | `LIFECYCLE_TRANSITION_INVALID`, `AUTHORITY_CONFLICT`, `APPROVAL_REQUIRED` |
|
|
126
|
+
| 4 | Mô hình lời hứa, actor và eligibility dưới chế độ model: claim fact gánh mọi hàng enforcing, claim intent gánh chính lời hứa | `promise` | `response/data/claims.json`, @workspaces/be ở head đóng băng, @tools/websearch | — | `EVIDENCE_MISSING` |
|
|
127
|
+
| 5 | Đóng băng ma trận phủ, dưới chế độ model | `dimensions` | `response/data/claims.json`, @workspaces/be và bề mặt nó phát hiện | `response/data/coverage-matrix.json` | `COVERAGE_INCOMPLETE`, `CONSUMER_UNPROVEN` |
|
|
128
|
+
| 6 | Xử lý phần chung sống với legacy, dưới chế độ model | — | `response/data/coverage-matrix.json`: các hàng legacy create, read, settle và bằng chứng của chúng | — | `CONTRADICTION_UNRESOLVED` |
|
|
129
|
+
| 7 | Đối chiếu với source đã giao, dưới chế độ reconcile | — | đầu vào `backend-source-application`, @workspaces/be ở head đóng băng, ma trận phủ đã đóng băng ở head đã publish | — | `RECONCILIATION_DISCREPANCY` |
|
|
130
|
+
| 8 | Publish một head dưới lease độc quyền | — | `response/data/claims.json`, @worktrees/businesses/<featureId> ở head trước | @worktrees/businesses/<featureId> thành model.json head mới, `response/data/model.json`, @tools/sourcewrite | `SOURCE_DRIFT` |
|
|
131
|
+
| 9 | Phát | — | mọi thứ ở trên | `response/response.md`, `response/response.json` | — |
|
|
132
|
+
|
|
133
|
+
Legacy create, read và settle mỗi thứ giữ một hàng riêng khi chúng được khai: một đường bán mới chỉ
|
|
134
|
+
được retire phần tạo legacy khi quyền đã mua vẫn đọc được và phần tất toán legacy còn treo vẫn hoàn
|
|
135
|
+
tất, và việc retire ấy mang bằng chứng rằng đường tạo đã đóng. Ma trận được địa chỉ theo nội dung và
|
|
136
|
+
fingerprint của nó đi trong binding, nên phần cài đặt backend, phần tích hợp chất lượng và UAT chứng
|
|
137
|
+
minh được rằng chúng tiêu thụ đúng ma trận ấy chứ không phải một bản diễn giải. Chạy lại thì bắt đầu
|
|
138
|
+
lại từ bước 1, chỉ dùng lại quan sát có fingerprint không đổi, và tiêu thụ đúng phần delta; bằng
|
|
139
|
+
chứng publish lại tới dưới dạng một fingerprint bằng chứng mới, vì cùng một fingerprint không thể cho
|
|
140
|
+
một câu trả lời khác.
|
|
141
|
+
|
|
142
|
+
## Đầu ra
|
|
143
|
+
|
|
144
|
+
| Kind | File | Kiểu | Bắt buộc |
|
|
145
|
+
| --- | --- | --- | --- |
|
|
146
|
+
| `business-promise-authority` | `response/response.md` | md | có |
|
|
147
|
+
| `claims` | `response/data/claims.json` | data | có |
|
|
148
|
+
| `coverage-matrix` | `response/data/coverage-matrix.json` | data | không |
|
|
149
|
+
| `model` | `response/data/model.json` | data | có |
|
|
150
|
+
|
|
151
|
+
## Dừng
|
|
152
|
+
|
|
153
|
+
| Code | Xử lý |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| `INVALID_INPUT` | terminate |
|
|
156
|
+
| `SOURCE_DRIFT` | terminate |
|
|
157
|
+
| `NO_PROGRESS` | terminate |
|
|
158
|
+
| `EVIDENCE_MISSING` | terminate |
|
|
159
|
+
| `CONTRADICTION_UNRESOLVED` | terminate |
|
|
160
|
+
| `LIFECYCLE_TRANSITION_INVALID` | terminate |
|
|
161
|
+
| `AUTHORITY_CONFLICT` | terminate |
|
|
162
|
+
| `APPROVAL_REQUIRED` | terminate |
|
|
163
|
+
| `COVERAGE_INCOMPLETE` | terminate |
|
|
164
|
+
| `CONSUMER_UNPROVEN` | terminate |
|
|
165
|
+
| `RECONCILIATION_DISCREPANCY` | terminate |
|
|
166
|
+
|
|
167
|
+
## Kế tiếp
|
|
168
|
+
|
|
169
|
+
| Khi | Operator |
|
|
170
|
+
| --- | --- |
|
|
171
|
+
| lời hứa đã publish và một bề mặt frontend phải mang nó | `frontend.direction.decide` |
|
|
172
|
+
| lời hứa đã publish và một contract backend phải mang nó | `backend.source.apply` |
|
|
173
|
+
| lời hứa cần quyết ranh giới và quyền sở hữu dữ liệu trước khi thực thi được | `architecture.decide` |
|
|
174
|
+
| head đã được đối chiếu với source đã giao và bản giao có thể được publish | `git.publish` |
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
// Proves validate.mjs on a synthetic session branch: one conforming publication of a first head under
|
|
2
|
+
// mode model, one reconcile pass against delivered source, one branch blocked on a terminate code, and
|
|
3
|
+
// one mutation per law, each of which must fail with a line that names the defect.
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { validateBusinessStep } from './validate.mjs';
|
|
9
|
+
|
|
10
|
+
const head = 'c'.repeat(40);
|
|
11
|
+
const claimsFingerprint = `sha256:${'a'.repeat(64)}`;
|
|
12
|
+
const coverageFingerprint = `sha256:${'b'.repeat(64)}`;
|
|
13
|
+
const ROOT_REF = '.worktrees/businesses';
|
|
14
|
+
const FEATURE = 'paid-access';
|
|
15
|
+
const CONSUMER = { consumerId: 'course-guard', dimension: 'entitlement-consumer', sourceRef: 'src/course/guard.ts' };
|
|
16
|
+
const DELIVERED = 'step-1/parallel-2/response/response.md';
|
|
17
|
+
|
|
18
|
+
const DISPOSITIONS = [
|
|
19
|
+
['actor-eligibility', 'preserve'], ['offer-entry', 'preserve'], ['read-entry', 'preserve'],
|
|
20
|
+
['purchase-side-effect', 'preserve'], ['settlement', 'preserve'], ['idempotency', 'preserve'],
|
|
21
|
+
['entitlement-consumer', 'preserve'], ['denial', 'preserve'], ['renewal', 'defer'],
|
|
22
|
+
['refund', 'not-applicable'], ['legacy-create', 'retire'], ['legacy-read', 'preserve'],
|
|
23
|
+
['legacy-settle', 'preserve'],
|
|
24
|
+
];
|
|
25
|
+
const DIMENSIONS = DISPOSITIONS.map(([d]) => d);
|
|
26
|
+
|
|
27
|
+
const proof = (name) => `tests/${name}.spec.ts`;
|
|
28
|
+
const row = (dimension, disposition, extra = {}) => {
|
|
29
|
+
const base = { dimension, disposition, statement: `how ${dimension} is answered`, enforcementOwner: null, sourceRef: null, positiveProofRef: null, negativeProofRef: null, deferralRef: null, consumerIds: [], claimIds: [] };
|
|
30
|
+
if (disposition === 'preserve' || disposition === 'replace') Object.assign(base, { enforcementOwner: 'entitlement', sourceRef: `src/${dimension}.ts`, positiveProofRef: proof(`${dimension}-granted`), negativeProofRef: proof(`${dimension}-denied`), claimIds: ['c-fact'] });
|
|
31
|
+
if (disposition === 'retire') Object.assign(base, { enforcementOwner: 'entitlement', sourceRef: `src/${dimension}.ts`, positiveProofRef: proof(`${dimension}-closed`), claimIds: ['c-fact'] });
|
|
32
|
+
if (disposition === 'defer') Object.assign(base, { deferralRef: `objective:${dimension}-later` });
|
|
33
|
+
return { ...base, ...extra };
|
|
34
|
+
};
|
|
35
|
+
function coverageMatrix(overrides = {}) {
|
|
36
|
+
return {
|
|
37
|
+
featureId: FEATURE,
|
|
38
|
+
fingerprint: coverageFingerprint,
|
|
39
|
+
dimensions: DIMENSIONS,
|
|
40
|
+
discoveredConsumers: [CONSUMER],
|
|
41
|
+
discoveredLifecycleBranches: ['renewal'],
|
|
42
|
+
rows: DISPOSITIONS.map(([dimension, disposition]) => row(dimension, disposition, dimension === 'entitlement-consumer' ? { consumerIds: [CONSUMER.consumerId] } : {})),
|
|
43
|
+
...overrides,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function withRow(dimension, patch) {
|
|
47
|
+
const matrix = coverageMatrix();
|
|
48
|
+
matrix.rows = matrix.rows.map((r) => (r.dimension === dimension ? { ...r, ...patch } : r));
|
|
49
|
+
return matrix;
|
|
50
|
+
}
|
|
51
|
+
const CLAIMS = [
|
|
52
|
+
{ claimId: 'c-fact', kind: 'fact', role: 'the guard that enforces the promise', path: 'src/entitlement/guard.ts', lineStart: 10, lineEnd: 24, sourceHead: head },
|
|
53
|
+
{ claimId: 'c-intent', kind: 'intent', role: "the owner's stated intent", path: 'docs/paid-access.md', lineStart: 3, lineEnd: 9, sourceHead: null },
|
|
54
|
+
];
|
|
55
|
+
const claimsDoc = (overrides = {}) => ({ featureId: FEATURE, sourceHead: head, fingerprint: claimsFingerprint, claims: CLAIMS, ...overrides });
|
|
56
|
+
|
|
57
|
+
function modelDoc({ mode = 'model', state = 'pending', transition = 'absent->pending', previousHeadRef = null, previousState = null, headRef = `${ROOT_REF}/features/${FEATURE}`, coverage = coverageFingerprint, reconciliation = null } = {}) {
|
|
58
|
+
return {
|
|
59
|
+
featureId: FEATURE, mode, headRef, headFingerprint: `sha256:${'d'.repeat(64)}`, state,
|
|
60
|
+
promise: { statement: 'a paying learner reads every course in the plan', actorStatement: 'a learner with a settled purchase', eligibilityStatement: 'the purchase settled and has not expired' },
|
|
61
|
+
lineage: { previousHeadRef, previousState, transition },
|
|
62
|
+
claimsFingerprint, coverageFingerprint: coverage, reconciliation,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function responseMd({ mode = 'model', state = 'pending', transition = 'absent->pending', previousHead = '—', previousState = '—', coverage = coverageFingerprint, claimsFp = claimsFingerprint, headRef = `${ROOT_REF}/features/${FEATURE}`, dispositions = DISPOSITIONS, reconciliation = [], findings = [], claims = [['c-fact', 'fact'], ['c-intent', 'intent']] } = {}) {
|
|
67
|
+
const coverageRows = dispositions.map(([dimension, disposition]) => `| \`${dimension}\` | ${disposition} | how ${dimension} is answered | ${dimension === 'entitlement-consumer' ? `\`${CONSUMER.consumerId}\`` : '—'} |`).join('\n');
|
|
68
|
+
const claimRows = claims.map(([id, kind]) => `| \`${id}\` | ${kind} | what ${id} observes | \`src/entitlement/guard.ts\` | 10-24 | ${kind === 'fact' ? `\`${head}\`` : '—'} |`).join('\n');
|
|
69
|
+
const reconciliationRows = reconciliation.map(([dimension, delivered, discrepancy]) => `| \`${dimension}\` | ${delivered} | ${discrepancy} |`).join('\n');
|
|
70
|
+
const findingRows = findings.map(([code, severity, dimension, statement]) => `| \`${code}\` | ${severity} | \`${dimension}\` | ${statement} |`).join('\n');
|
|
71
|
+
return `# business-promise-authority — ${FEATURE}
|
|
72
|
+
|
|
73
|
+
The paid access promise, decided from the frozen evidence of the routed backend checkout.
|
|
74
|
+
|
|
75
|
+
## Binding
|
|
76
|
+
|
|
77
|
+
| Field | Value |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| Feature | \`${FEATURE}\` |
|
|
80
|
+
| Mode | ${mode} |
|
|
81
|
+
| Target state | ${state} |
|
|
82
|
+
| Head | \`${headRef}\` |
|
|
83
|
+
| Claims fingerprint | \`${claimsFp}\` |
|
|
84
|
+
| Coverage fingerprint | \`${coverage}\` |
|
|
85
|
+
|
|
86
|
+
## Promise
|
|
87
|
+
|
|
88
|
+
| Field | Value |
|
|
89
|
+
| --- | --- |
|
|
90
|
+
| Promise | a paying learner reads every course in the plan |
|
|
91
|
+
| Actor | a learner with a settled purchase |
|
|
92
|
+
| Eligibility | the purchase settled and has not expired |
|
|
93
|
+
|
|
94
|
+
## Lineage
|
|
95
|
+
|
|
96
|
+
| Field | Value |
|
|
97
|
+
| --- | --- |
|
|
98
|
+
| Previous head | ${previousHead} |
|
|
99
|
+
| Previous state | ${previousState} |
|
|
100
|
+
| Transition | ${transition} |
|
|
101
|
+
|
|
102
|
+
## Cited claims
|
|
103
|
+
|
|
104
|
+
| Claim | Kind | Role | Source | Lines | Head |
|
|
105
|
+
| --- | --- | --- | --- | --- | --- |
|
|
106
|
+
${claimRows}
|
|
107
|
+
|
|
108
|
+
## Coverage
|
|
109
|
+
|
|
110
|
+
| Dimension | Disposition | Statement | Consumers |
|
|
111
|
+
| --- | --- | --- | --- |
|
|
112
|
+
${coverageRows}${coverageRows ? '\n' : ''}
|
|
113
|
+
## Reconciliation
|
|
114
|
+
|
|
115
|
+
| Dimension | Delivered evidence | Discrepancy |
|
|
116
|
+
| --- | --- | --- |
|
|
117
|
+
${reconciliationRows}${reconciliationRows ? '\n' : ''}
|
|
118
|
+
## Findings
|
|
119
|
+
|
|
120
|
+
| Code | Severity | Dimension | Statement |
|
|
121
|
+
| --- | --- | --- | --- |
|
|
122
|
+
${findingRows}${findingRows ? '\n' : ''}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const requestJson = ({ mode = 'model', targetState = 'pending', inputs = {}, extra = {} } = {}) => ({
|
|
126
|
+
schemaVersion: 9, operatorId: 'business.decide', step: 1, parallel: 1, sessionId: 's-test',
|
|
127
|
+
contexts: [{ alias: '@workspaces/be', head }, { alias: '@worktrees/businesses/paid-access', head: null }],
|
|
128
|
+
requirements: { featureId: FEATURE, mode, targetState, dimensions: DIMENSIONS, approval: null, resume: null, ...extra },
|
|
129
|
+
inputs, resume: null,
|
|
130
|
+
});
|
|
131
|
+
function responseJson({ status = 'done', stop, fallbacks = [], fields = null, next = ['backend.source.apply'] } = {}) {
|
|
132
|
+
return {
|
|
133
|
+
schemaVersion: 9, operatorId: 'business.decide', step: 1, parallel: 1, status, ...(stop ? { stop } : {}), fallbacks,
|
|
134
|
+
fields: fields ?? { 'business-promise-authority': 'response/response.md', claims: 'response/data/claims.json', 'coverage-matrix': 'response/data/coverage-matrix.json', model: 'response/data/model.json' },
|
|
135
|
+
commits: [], next,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function writeBranch(files) {
|
|
140
|
+
const session = mkdtempSync(path.join(tmpdir(), 'business-session-'));
|
|
141
|
+
const branch = path.join(session, 'step-1', 'parallel-1');
|
|
142
|
+
for (const d of ['request', 'response/data', 'response/artifacts']) mkdirSync(path.join(branch, d), { recursive: true });
|
|
143
|
+
mkdirSync(path.join(session, 'step-1', 'parallel-2', 'response'), { recursive: true });
|
|
144
|
+
writeFileSync(path.join(session, DELIVERED), '# backend-source-application — delivered source\n');
|
|
145
|
+
writeFileSync(path.join(session, 'state.json'), JSON.stringify({ id: 's-test', chain: [['1/1']], steps: { '1/1': 'business.decide' }, current: '1/1', status: 'running' }));
|
|
146
|
+
for (const [name, content] of Object.entries(files)) {
|
|
147
|
+
if (content === null) continue;
|
|
148
|
+
writeFileSync(path.join(branch, name), typeof content === 'string' ? content : JSON.stringify(content, null, 2));
|
|
149
|
+
}
|
|
150
|
+
return { branch, session };
|
|
151
|
+
}
|
|
152
|
+
const baseline = () => ({
|
|
153
|
+
'request/request.json': requestJson(),
|
|
154
|
+
'response/response.json': responseJson(),
|
|
155
|
+
'response/response.md': responseMd(),
|
|
156
|
+
'response/data/claims.json': claimsDoc(),
|
|
157
|
+
'response/data/coverage-matrix.json': coverageMatrix(),
|
|
158
|
+
'response/data/model.json': modelDoc(),
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
async function expectValid(files, label) {
|
|
162
|
+
const { branch, session } = writeBranch(files);
|
|
163
|
+
const { errors } = await validateBusinessStep(branch);
|
|
164
|
+
rmSync(session, { recursive: true, force: true });
|
|
165
|
+
assert.deepEqual(errors, [], `${label} should be valid`);
|
|
166
|
+
}
|
|
167
|
+
async function expectError(files, needle, label) {
|
|
168
|
+
const { branch, session } = writeBranch(files);
|
|
169
|
+
const { errors } = await validateBusinessStep(branch);
|
|
170
|
+
rmSync(session, { recursive: true, force: true });
|
|
171
|
+
assert.ok(errors.some((e) => e.includes(needle)), `${label}: expected an error containing "${needle}", got:\n${errors.join('\n') || '(none)'}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const RECONCILE_FIELDS = { 'business-promise-authority': 'response/response.md', claims: 'response/data/claims.json', model: 'response/data/model.json' };
|
|
175
|
+
const reconciled = (overrides = {}) => ({
|
|
176
|
+
...baseline(),
|
|
177
|
+
'request/request.json': requestJson({ mode: 'reconcile', targetState: 'implemented', inputs: { 'backend-source-application': DELIVERED } }),
|
|
178
|
+
'response/response.json': responseJson({ fields: RECONCILE_FIELDS }),
|
|
179
|
+
'response/response.md': responseMd({ mode: 'reconcile', state: 'implemented', transition: 'in-progress->implemented', previousHead: `\`${ROOT_REF}/features/${FEATURE}\``, previousState: 'in-progress', coverage: coverageFingerprint, dispositions: [], reconciliation: [['entitlement-consumer', '`src/course/guard.ts`', '—']] }),
|
|
180
|
+
'response/data/coverage-matrix.json': null,
|
|
181
|
+
'response/data/model.json': modelDoc({ mode: 'reconcile', state: 'implemented', transition: 'in-progress->implemented', previousHeadRef: `${ROOT_REF}/features/${FEATURE}`, previousState: 'in-progress', reconciliation: { deliveredEvidenceRefs: ['src/course/guard.ts'], discrepancies: [] } }),
|
|
182
|
+
...overrides,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
await expectValid(baseline(), 'a first publication of one feature head under mode model');
|
|
186
|
+
await expectValid(reconciled(), 'a reconcile pass against delivered source');
|
|
187
|
+
await expectValid({ ...baseline(), 'response/response.json': responseJson({ status: 'blocked', stop: 'CONSUMER_UNPROVEN', fields: {}, next: [] }), 'response/response.md': null, 'response/data/claims.json': null, 'response/data/coverage-matrix.json': null, 'response/data/model.json': null }, 'blocked on an undisposed consumer');
|
|
188
|
+
|
|
189
|
+
await expectError({ ...baseline(), 'response/response.json': { ...responseJson(), stop: 'CONSUMER_UNPROVEN' } }, 'only a blocked response carries a stop', 'done with a stop');
|
|
190
|
+
await expectError({ ...baseline(), 'response/response.json': responseJson({ status: 'blocked', stop: 'MADE_UP_CODE', fields: {}, next: [] }), 'response/response.md': null, 'response/data/claims.json': null, 'response/data/coverage-matrix.json': null, 'response/data/model.json': null }, 'not a registered code', 'unknown stop code');
|
|
191
|
+
await expectError({ ...baseline(), 'response/response.json': responseJson({ fallbacks: ['COVERAGE_INCOMPLETE'] }) }, 'has disposition terminate under these requirements; it cannot be taken as a fallback', 'fallback on a terminate code');
|
|
192
|
+
await expectError({ ...baseline(), 'response/response.json': responseJson({ status: 'blocked', stop: 'CONSUMER_UNPROVEN', next: [] }) }, 'a blocked branch cannot freeze a coverage matrix', 'blocked while freezing the matrix');
|
|
193
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ mode: 'reconcile', targetState: 'implemented' }) }, 'input backend-source-application is required', 'reconcile with no delivered source');
|
|
194
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ inputs: { 'backend-source-application': DELIVERED } }) }, 'input backend-source-application is refused', 'model mode reading delivered source');
|
|
195
|
+
await expectError(reconciled({ 'response/response.json': responseJson({ fields: { ...RECONCILE_FIELDS, 'coverage-matrix': 'response/data/coverage-matrix.json' } }), 'response/data/coverage-matrix.json': coverageMatrix() }), 'may not freeze a new coverage matrix', 'reconcile freezing a matrix');
|
|
196
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { mystery: 1 } }) }, 'requirements.mystery is not a field', 'undeclared requirement');
|
|
197
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { featureId: '' } }) }, 'required field featureId has no value', 'missing required featureId');
|
|
198
|
+
await expectError({ ...baseline(), 'response/data/model.json': modelDoc({ headRef: `${ROOT_REF}/features/starci/${FEATURE}` }) }, 'no project segment below the businesses root', 'a project segment below the businesses root');
|
|
199
|
+
await expectError({ ...baseline(), 'response/data/model.json': modelDoc({ headRef: `.worktrees/authority/features/${FEATURE}` }) }, 'is not under a .worktrees/businesses root', 'head outside the businesses root');
|
|
200
|
+
await expectError({ ...baseline(), 'response/data/model.json': modelDoc({ coverage: `sha256:${'e'.repeat(64)}` }) }, 'must equal the frozen matrix fingerprint', 'model and matrix disagree on the fingerprint');
|
|
201
|
+
await expectError({ ...baseline(), 'response/response.md': responseMd({ coverage: `sha256:${'e'.repeat(64)}` }) }, 'must equal the coverage matrix fingerprint', 'response and matrix disagree on the fingerprint');
|
|
202
|
+
await expectError({ ...baseline(), 'response/data/model.json': modelDoc({ transition: 'pending->in-progress' }) }, 'contradicts previous state null', 'transition contradicts the previous state');
|
|
203
|
+
await expectError({ ...baseline(), 'response/data/model.json': modelDoc({ previousHeadRef: `${ROOT_REF}/features/${FEATURE}` }) }, 'a first publication cannot name a previous head', 'first publication with lineage');
|
|
204
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ targetState: 'implemented' }), 'response/data/model.json': modelDoc({ state: 'implemented', transition: 'in-progress->implemented', previousHeadRef: `${ROOT_REF}/features/${FEATURE}`, previousState: 'in-progress' }), 'response/response.md': responseMd({ state: 'implemented', transition: 'in-progress->implemented', previousHead: `\`${ROOT_REF}/features/${FEATURE}\``, previousState: 'in-progress' }) }, 'an implemented head requires reconciliation against delivered source', 'implemented without reconciliation');
|
|
205
|
+
await expectError(reconciled({ 'response/data/model.json': modelDoc({ mode: 'reconcile', state: 'implemented', transition: 'in-progress->implemented', previousHeadRef: `${ROOT_REF}/features/${FEATURE}`, previousState: 'in-progress', reconciliation: { deliveredEvidenceRefs: ['src/settlement.ts'], discrepancies: [{ dimension: 'settlement', statement: 'the webhook still writes the legacy row' }] } }) }), 'reconciliation discrepancy or discrepancies remain', 'reconcile published with a discrepancy');
|
|
206
|
+
await expectError({ ...baseline(), 'response/data/coverage-matrix.json': withRow('denial', { disposition: 'not-applicable', enforcementOwner: null, sourceRef: null, positiveProofRef: null, negativeProofRef: null, claimIds: [] }), 'response/response.md': responseMd({ dispositions: DISPOSITIONS.map(([d, p]) => (d === 'denial' ? [d, 'not-applicable'] : [d, p])) }) }, 'is mandatory for a published promise and cannot be marked not-applicable', 'mandatory dimension not applicable');
|
|
207
|
+
await expectError({ ...baseline(), 'response/data/coverage-matrix.json': withRow('renewal', { disposition: 'not-applicable', deferralRef: null }), 'response/response.md': responseMd({ dispositions: DISPOSITIONS.map(([d, p]) => (d === 'renewal' ? [d, 'not-applicable'] : [d, p])) }) }, 'was discovered in the source and cannot be marked not-applicable', 'discovered lifecycle branch not applicable');
|
|
208
|
+
await expectError({ ...baseline(), 'response/data/coverage-matrix.json': withRow('settlement', { negativeProofRef: null }) }, 'has no negative proof', 'preserve without a negative proof');
|
|
209
|
+
await expectError({ ...baseline(), 'response/data/coverage-matrix.json': withRow('renewal', { positiveProofRef: proof('renewal-granted') }) }, 'cannot claim proof for work that has not happened', 'deferred row claiming proof');
|
|
210
|
+
await expectError({ ...baseline(), 'response/data/coverage-matrix.json': withRow('settlement', { claimIds: ['c-intent'] }) }, 'without one fact claim', 'enforcement resting on an intent claim');
|
|
211
|
+
await expectError({ ...baseline(), 'response/data/coverage-matrix.json': withRow('settlement', { claimIds: ['c-ghost'] }) }, 'which claims.json does not carry', 'row citing an unlisted claim');
|
|
212
|
+
await expectError({ ...baseline(), 'response/data/coverage-matrix.json': withRow('entitlement-consumer', { consumerIds: [] }) }, 'has no disposition in the coverage matrix', 'discovered consumer never disposed');
|
|
213
|
+
await expectError({ ...baseline(), 'response/data/coverage-matrix.json': withRow('denial', { consumerIds: [CONSUMER.consumerId] }) }, 'is disposed in more than one row', 'consumer disposed twice');
|
|
214
|
+
await expectError({ ...baseline(), 'response/data/coverage-matrix.json': withRow('offer-entry', { consumerIds: ['ghost-guard'] }) }, 'was never discovered, so no evidence supports it', 'row disposing an undiscovered consumer');
|
|
215
|
+
await expectError({ ...baseline(), 'response/data/coverage-matrix.json': coverageMatrix({ rows: coverageMatrix().rows.slice(0, 12) }), 'response/response.md': responseMd({ dispositions: DISPOSITIONS.slice(0, 12) }) }, 'has no disposition', 'a declared dimension missing from the matrix');
|
|
216
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { dimensions: DIMENSIONS.slice(0, 12) } }) }, 'was never declared by the request', 'a matrix dimension nobody declared');
|
|
217
|
+
await expectError({ ...baseline(), 'response/response.md': responseMd({ dispositions: DISPOSITIONS.map(([d, p]) => (d === 'legacy-read' ? [d, 'defer'] : [d, p])) }) }, 'is defer here and preserve in the matrix', 'the response and the matrix disagree on a disposition');
|
|
218
|
+
await expectError({ ...baseline(), 'response/response.md': responseMd().replace('## Coverage', '## Coverage matrix') }, 'missing section ^## Coverage$', 'response section renamed');
|
|
219
|
+
await expectError({ ...baseline(), 'response/response.md': responseMd({ findings: [['LEGACY_COEXISTENCE', 'error', 'legacy-create', 'the legacy sale path still creates rights']] }) }, 'is still an open error, so the promise cannot be published', 'published with an open error finding');
|
|
220
|
+
await expectError({ ...baseline(), 'response/data/claims.json': claimsDoc({ claims: [CLAIMS[0], { ...CLAIMS[0], kind: 'intent' }] }) }, 'is declared more than once', 'a duplicated claim');
|
|
221
|
+
await expectError({ ...baseline(), 'response/data/claims.json': claimsDoc({ claims: [{ ...CLAIMS[0], sourceHead: null }, CLAIMS[1]] }) }, 'must bind the observed source head', 'a fact claim with no head');
|
|
222
|
+
await expectError({ ...baseline(), 'response/response.md': responseMd({ claims: [['c-fact', 'fact']] }) }, 'which Cited claims omits', 'a claim the response never cites');
|
|
223
|
+
await expectError({ ...baseline(), 'response/response.json': (() => { const o = responseJson(); delete o.fields.model; return o; })() }, 'required output model is not in fields', 'missing required output');
|
|
224
|
+
|
|
225
|
+
process.stdout.write('business.decide self-test: 3 valid branches, 33 rejected mutations\n');
|