@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,167 @@
|
|
|
1
|
+
# frontend.direction.decide
|
|
2
|
+
|
|
3
|
+
## Job
|
|
4
|
+
|
|
5
|
+
Decide one evidence-backed, implementation-ready frontend direction for one authorized target, and
|
|
6
|
+
prove it against the business promise, the published Grammar, the observed implementation and a
|
|
7
|
+
falsification pass that no candidate survives by taste.
|
|
8
|
+
|
|
9
|
+
## The change level decides what must be bound
|
|
10
|
+
|
|
11
|
+
The change level is the request's own authority and current source never proves it: an audit that
|
|
12
|
+
asks for a passing surface is `reconstruct`, not `refine`. `new` requires the business promise, and
|
|
13
|
+
it closes the state set and the exits before anything is drawn. `reconstruct` requires the promise
|
|
14
|
+
only when the state set changes, and it preserves the business facts, the behaviour authority and
|
|
15
|
+
the API semantics it inherits. `refine` requires no input authority at all, because it changes
|
|
16
|
+
nothing a promise could contradict. A backend implementation is required when a data contract
|
|
17
|
+
changes, an architecture decision when a boundary changes; neither is required otherwise, and
|
|
18
|
+
neither is ever invented. `create` occurs with `new` and only with it.
|
|
19
|
+
|
|
20
|
+
## Evidence contradicts, it does not authorize
|
|
21
|
+
|
|
22
|
+
The current implementation, a green test, a rendered DOM and a prior UAT pass are all evidence, and
|
|
23
|
+
each observation is recorded as a path at the head it was read at. Evidence must be observed before
|
|
24
|
+
any proposal is written: the target's direct artifacts without the producer's rationale, or, for a
|
|
25
|
+
new target, proof that the target is absent and only the authorized host and product-family context.
|
|
26
|
+
None of it authorizes a direction by incumbency. A page that exists is not a reason to build that
|
|
27
|
+
page again.
|
|
28
|
+
|
|
29
|
+
## Bounded research, and only where it is owed
|
|
30
|
+
|
|
31
|
+
External references are resolved only when the person supplied none and the change level is `new` or
|
|
32
|
+
`reconstruct`. A `refine` works from the family idioms alone. Every reference that survives is
|
|
33
|
+
recorded with its URL and with the exact limitation it carries; nothing copies a page, a brand, a
|
|
34
|
+
palette or a component anatomy. When bounded research cannot close the business or interaction
|
|
35
|
+
question the decision rests on, the run stops with the owning gap.
|
|
36
|
+
|
|
37
|
+
## The Grammar filter refuses invention, not ownership
|
|
38
|
+
|
|
39
|
+
A candidate that invents a missing shared interface, bypasses the owner ceiling, imitates unpublished
|
|
40
|
+
Grammar locally or contradicts a published composition is rejected. `GRAMMAR_REQUIRED` is only for a
|
|
41
|
+
missing family component, and it goes to a person who publishes it; the operator never composes a
|
|
42
|
+
substitute out of parts. A node the application legitimately owns, a canvas for instance, is not a
|
|
43
|
+
Grammar gap and never raises one.
|
|
44
|
+
|
|
45
|
+
## Candidates are falsified before they are chosen
|
|
46
|
+
|
|
47
|
+
Falsification attacks business and backend conformance, hierarchy, content density, action feedback,
|
|
48
|
+
recovery, responsive reflow, content stress, keyboard and focus behaviour, accessibility, family
|
|
49
|
+
coherence, reversibility and owner leakage, and every attack lands in the receipt with its verdict.
|
|
50
|
+
A direction is invalid while an applicable business contradiction, owner leak, Grammar invention,
|
|
51
|
+
responsive failure, accessibility failure, unresolved adverse state or materially stronger reversible
|
|
52
|
+
alternative remains. Under `refine` the candidates are element-level moves inside the approved
|
|
53
|
+
structure, never a new structure. When the only candidate fails an attack the run stops with
|
|
54
|
+
`NO_VIABLE_DIRECTION`, and when several survive, `DIRECTION_CHOICE_REQUIRED` selects the one that
|
|
55
|
+
survived the most attacks unless the person kept the choice.
|
|
56
|
+
|
|
57
|
+
## Boundary
|
|
58
|
+
|
|
59
|
+
Context is read-only. The operator writes only `response/` of its own branch: the decision receipt,
|
|
60
|
+
the coverage enumeration, the rendered candidate pages and `response.json`. It does not modify
|
|
61
|
+
product or authority source, invent business, backend, architecture, authentication, persistence or
|
|
62
|
+
data behaviour, publish shared UI Grammar, start or reconfigure runtime services, or claim that an
|
|
63
|
+
implementation, a visual quality gate or a UAT run has passed.
|
|
64
|
+
|
|
65
|
+
## Images are judged, not requested
|
|
66
|
+
|
|
67
|
+
An image is a composition decision like any other: when a candidate leaves a region that reads empty
|
|
68
|
+
(a hero without a subject, an empty state with only a sentence, a card row whose copy cannot carry the
|
|
69
|
+
width), the operator adds an image made to one stated claim of the direction and records why, in the
|
|
70
|
+
`## Images` table. It does not wait for a person to ask, and it does not decorate: a region that the
|
|
71
|
+
copy and the Grammar objects already carry gets no image, and an image never encodes a claim the
|
|
72
|
+
business promise did not make. The asset and its prompt land under `response/artifacts/images/`;
|
|
73
|
+
`frontend.source.apply` writes them with the declared write set.
|
|
74
|
+
|
|
75
|
+
## Context
|
|
76
|
+
|
|
77
|
+
| Alias | Bind | Required |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| `@grammar/core` | the published Grammar as the bound app resolves it; the compositions a direction may bind | yes |
|
|
80
|
+
| `@knowledge/ui/composition` | the assertions the emitted receipt has to satisfy, `COVERAGE-1` being the assertion about the receipt as a whole | yes |
|
|
81
|
+
| `@workspaces/fe` | the routed frontend checkout read at the frozen head; the current implementation as evidence, never as the requested direction | yes |
|
|
82
|
+
| `@knowledge/grammars/starci` | how the Core family is meant to realize Common; law about the Grammar, never the Grammar itself | no |
|
|
83
|
+
| `@worktrees/uat/<flow>/<case>` | prior behaviour, UX and UI observations with their captures; evidence and counterevidence, and a prior pass is not current authority | no |
|
|
84
|
+
|
|
85
|
+
## Inputs
|
|
86
|
+
|
|
87
|
+
| Kind | From | Required |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| `business-promise-authority` | `business.decide`; required by `new`, and by `reconstruct` when the state set changes | no |
|
|
90
|
+
| `backend-source-application` | `backend.source.apply`; required when a data contract changes | no |
|
|
91
|
+
| `architecture-decision` | `architecture.decide`; required when a boundary changes | no |
|
|
92
|
+
| `frontend-direction-decision` | a prior run of `frontend.direction.decide` on the same target, read when resuming | no |
|
|
93
|
+
|
|
94
|
+
## Requirements
|
|
95
|
+
|
|
96
|
+
| Field | Type | Default | Ask |
|
|
97
|
+
| --- | --- | --- | --- |
|
|
98
|
+
| `target` | id | — | The one route, page, layout, modal, drawer, flow, block or component this direction is for |
|
|
99
|
+
| `intent` | choice | modify | create, modify, audit-repair or reconcile; `create` occurs with change level `new` and only with it |
|
|
100
|
+
| `changeLevel` | choice | — | new, reconstruct or refine; an audit that must end in a passing surface is reconstruct |
|
|
101
|
+
| `ownerCeiling` | choice | surface-and-nested-layouts | surface-only, surface-and-nested-layouts or ancestor-layouts-authorized |
|
|
102
|
+
| `candidates` | number 1–3 | 1 | How many directions to form; more than one only when a comparison is wanted |
|
|
103
|
+
| `preview` | choice | no | yes renders the single candidate as an inspectable page |
|
|
104
|
+
| `references` | list | [] | External references the person supplies; bounded research runs only when this is empty |
|
|
105
|
+
| `selectionPolicy` | choice | automatic | `automatic`: the operator selects and records why; `approval-required`: the person selects |
|
|
106
|
+
| `approval` | id | null | The approved candidate id; required only under `approval-required`, supplied on resume after `DIRECTION_CHOICE_REQUIRED` |
|
|
107
|
+
| `resume` | token | null | The blocked branch's token when re-entering after a stop |
|
|
108
|
+
|
|
109
|
+
## Steps
|
|
110
|
+
|
|
111
|
+
| # | Step | Params | Reads | Writes | Stops with |
|
|
112
|
+
| --- | --- | --- | --- | --- | --- |
|
|
113
|
+
| 1 | Validate the gate and resume | `resume`, `approval` | `request/request.json`, input `frontend-direction-decision` when resuming, @workspaces/fe at the frozen head | — | `INVALID_INPUT`, `SOURCE_DRIFT`, `NO_PROGRESS` |
|
|
114
|
+
| 2 | Check the request: route, scope, change level, owner ceiling | `target`, `intent`, `changeLevel`, `ownerCeiling` | @workspaces/fe at the frozen head, @tools/git | — | `ROUTE_UNVERIFIED`, `SCOPE_UNFROZEN`, `CHANGE_LEVEL_AMBIGUOUS`, `OWNER_CEILING_INVALID` |
|
|
115
|
+
| 3 | Bind the inputs the change level requires | `changeLevel` | inputs `business-promise-authority`, `backend-source-application` and `architecture-decision` | — | `BUSINESS_REQUIRED`, `BACKEND_REQUIRED`, `ARCHITECTURE_REQUIRED` |
|
|
116
|
+
| 4 | Observe the existing context | — | @workspaces/fe (the target's direct artifacts, or the authorized host and product family when the target is absent), @worktrees/uat/<flow>/<case> when present | — | `EVIDENCE_MISSING` |
|
|
117
|
+
| 5 | Compile one UI contract and its coverage | — | @knowledge/ui/composition, input `business-promise-authority` when present, the observed context | `response/data/coverage.json` | `SCOPE_UNFROZEN` |
|
|
118
|
+
| 6 | Resolve external references, bounded | `references`, `changeLevel` | @knowledge/ui/composition (the gap the research must close), @tools/websearch | — | `REFERENCE_EVIDENCE_EXHAUSTED` |
|
|
119
|
+
| 7 | Form the candidates | `candidates` | the compiled UI contract | — | `NO_VIABLE_DIRECTION` |
|
|
120
|
+
| 8 | Apply the Grammar filter | `ownerCeiling` | @grammar/core (what a component owns and which props exist), @knowledge/grammars/starci | — | `GRAMMAR_REQUIRED` |
|
|
121
|
+
| 9 | Render the decision evidence and the judged images | `candidates`, `preview` | the surviving candidates, `@knowledge/grammars/starci` | `response/artifacts/<candidateId>.html`, `response/artifacts/images/<slot>.png`, @tools/visualize, @tools/imagegen | — |
|
|
122
|
+
| 10 | Falsify | — | the candidates, inputs `business-promise-authority` and `backend-source-application`, `response/data/coverage.json` | — | `NO_VIABLE_DIRECTION` |
|
|
123
|
+
| 11 | Decide | `selectionPolicy`, `approval` | the falsification table | — | `DIRECTION_CHOICE_REQUIRED` |
|
|
124
|
+
| 12 | Emit | — | everything above | `response/response.md`, `response/response.json` | — |
|
|
125
|
+
|
|
126
|
+
Step 9 renders only when more than one candidate was formed or `preview` is yes; a single candidate
|
|
127
|
+
under the defaults produces no page and rests on step 10. Under `automatic`,
|
|
128
|
+
`DIRECTION_CHOICE_REQUIRED` selects the candidate that survived the most attacks and records the
|
|
129
|
+
table; under `approval-required` it stops and the person returns with `approval`. The receipt
|
|
130
|
+
authorizes the next domain to resolve and implement inside the frozen owner ceiling and proves
|
|
131
|
+
nothing about how the result renders.
|
|
132
|
+
|
|
133
|
+
## Outputs
|
|
134
|
+
|
|
135
|
+
| Kind | File | Type | Required |
|
|
136
|
+
| --- | --- | --- | --- |
|
|
137
|
+
| `frontend-direction-decision` | `response/response.md` | md | yes |
|
|
138
|
+
| `ui-coverage` | `response/data/coverage.json` | data | yes |
|
|
139
|
+
| `candidates` | `response/artifacts/<candidateId>.html` | artifact | no |
|
|
140
|
+
| `direction-image` | `response/artifacts/images/<slot>.png` | artifact | no |
|
|
141
|
+
|
|
142
|
+
## Stops
|
|
143
|
+
|
|
144
|
+
| Code | Disposition |
|
|
145
|
+
| --- | --- |
|
|
146
|
+
| `INVALID_INPUT` | terminate |
|
|
147
|
+
| `ROUTE_UNVERIFIED` | terminate |
|
|
148
|
+
| `SOURCE_DRIFT` | terminate |
|
|
149
|
+
| `SCOPE_UNFROZEN` | terminate |
|
|
150
|
+
| `CHANGE_LEVEL_AMBIGUOUS` | terminate |
|
|
151
|
+
| `OWNER_CEILING_INVALID` | terminate |
|
|
152
|
+
| `BUSINESS_REQUIRED` | terminate |
|
|
153
|
+
| `BACKEND_REQUIRED` | terminate |
|
|
154
|
+
| `ARCHITECTURE_REQUIRED` | terminate |
|
|
155
|
+
| `GRAMMAR_REQUIRED` | terminate |
|
|
156
|
+
| `EVIDENCE_MISSING` | terminate |
|
|
157
|
+
| `REFERENCE_EVIDENCE_EXHAUSTED` | terminate |
|
|
158
|
+
| `NO_VIABLE_DIRECTION` | terminate |
|
|
159
|
+
| `DIRECTION_CHOICE_REQUIRED` | fallback |
|
|
160
|
+
| `NO_PROGRESS` | terminate |
|
|
161
|
+
|
|
162
|
+
## Next
|
|
163
|
+
|
|
164
|
+
| When | Operator |
|
|
165
|
+
| --- | --- |
|
|
166
|
+
| the direction is decided; every direction resolves its presentation values before any source is written | `frontend.presentation.resolve` |
|
|
167
|
+
| a family component the direction needs is unpublished, so a person publishes it and the same direction runs again | `frontend.direction.decide` |
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frontend.direction.decide
|
|
2
|
+
|
|
3
|
+
## Việc
|
|
4
|
+
|
|
5
|
+
Quyết một hướng frontend có bằng chứng, đủ để triển khai, cho đúng một target được uỷ quyền, rồi
|
|
6
|
+
chứng minh nó bằng lời hứa nghiệp vụ, Grammar đã publish, hiện trạng quan sát được và một lượt phản
|
|
7
|
+
chứng mà không phương án nào qua được bằng khẩu vị.
|
|
8
|
+
|
|
9
|
+
## Change level quyết định phải bind những gì
|
|
10
|
+
|
|
11
|
+
Change level là thẩm quyền của chính lời yêu cầu, và source hiện tại không bao giờ chứng minh được
|
|
12
|
+
nó: một lượt audit đòi bề mặt phải pass là `reconstruct`, không phải `refine`. `new` cần lời hứa
|
|
13
|
+
nghiệp vụ, và nó đóng tập trạng thái cùng các lối ra trước khi vẽ bất cứ thứ gì. `reconstruct` chỉ
|
|
14
|
+
cần lời hứa khi tập trạng thái đổi, và nó giữ nguyên các sự thật nghiệp vụ, thẩm quyền hành vi và
|
|
15
|
+
ngữ nghĩa API mà nó thừa hưởng. `refine` không cần thẩm quyền đầu vào nào cả, vì nó không đổi thứ gì
|
|
16
|
+
mà một lời hứa có thể phản bác. Backend implementation là bắt buộc khi một contract dữ liệu đổi,
|
|
17
|
+
architecture decision khi một ranh giới đổi; ngoài ra không bắt buộc, và không bao giờ được bịa ra.
|
|
18
|
+
`create` đi cùng `new` và chỉ đi cùng nó.
|
|
19
|
+
|
|
20
|
+
## Bằng chứng phản bác, chứ không uỷ quyền
|
|
21
|
+
|
|
22
|
+
Hiện trạng implementation, một test xanh, DOM đã render và một lần UAT pass trước đều là bằng chứng,
|
|
23
|
+
và mỗi quan sát được ghi lại thành một path kèm head lúc đọc. Bằng chứng phải được quan sát trước khi
|
|
24
|
+
viết bất kỳ đề xuất nào: lấy các artifact trực tiếp của target mà bỏ lý lẽ của người tạo ra chúng,
|
|
25
|
+
hoặc, với target mới, chứng minh target chưa tồn tại rồi chỉ quan sát host được uỷ quyền và context
|
|
26
|
+
cùng họ sản phẩm. Không mảnh nào trong số đó uỷ quyền cho một hướng chỉ vì nó đang chạy sẵn. Một
|
|
27
|
+
trang đang tồn tại không phải lý do để dựng lại đúng trang ấy.
|
|
28
|
+
|
|
29
|
+
## Nghiên cứu có giới hạn, và chỉ ở chỗ còn nợ
|
|
30
|
+
|
|
31
|
+
Tham chiếu ngoài chỉ được đi tìm khi người dùng không cấp cái nào và change level là `new` hay
|
|
32
|
+
`reconstruct`. Một lượt `refine` làm việc bằng idiom của họ là đủ. Mỗi tham chiếu còn lại được ghi
|
|
33
|
+
kèm URL và đúng giới hạn nó mang theo; không gì chép lại một trang, một thương hiệu, một bảng màu hay
|
|
34
|
+
giải phẫu của một component. Khi nghiên cứu có giới hạn không lấp được câu hỏi nghiệp vụ hay tương
|
|
35
|
+
tác mà quyết định dựa vào, lần chạy dừng ở đúng khoảng trống có chủ.
|
|
36
|
+
|
|
37
|
+
## Bộ lọc Grammar từ chối sự bịa đặt, không từ chối quyền sở hữu
|
|
38
|
+
|
|
39
|
+
Một phương án bịa ra giao diện dùng chung còn thiếu, đi vòng qua trần owner, bắt chước Grammar chưa
|
|
40
|
+
publish ngay tại chỗ hay chỏi một composition đã publish thì bị loại. `GRAMMAR_REQUIRED` chỉ dành cho
|
|
41
|
+
một component của họ còn thiếu, và nó đi tới một con người để publish; operator không bao giờ ghép
|
|
42
|
+
tạm một cái thay thế từ các mảnh. Một node mà ứng dụng sở hữu hợp lệ, chẳng hạn một canvas, không
|
|
43
|
+
phải khoảng trống Grammar và không bao giờ làm phát mã ấy.
|
|
44
|
+
|
|
45
|
+
## Phương án bị phản chứng trước khi được chọn
|
|
46
|
+
|
|
47
|
+
Phản chứng tấn công vào tính phù hợp với nghiệp vụ và backend, thứ bậc, mật độ nội dung, phản hồi
|
|
48
|
+
hành động, đường phục hồi, dòng chảy responsive, sức chịu nội dung, bàn phím và focus, khả năng tiếp
|
|
49
|
+
cận, tính nhất quán của họ, khả năng đảo ngược và rò rỉ quyền sở hữu, và mỗi đòn tấn công đều rơi vào
|
|
50
|
+
biên nhận kèm phán quyết của nó. Một hướng còn vô hiệu chừng nào còn một mâu thuẫn nghiệp vụ áp dụng
|
|
51
|
+
được, một rò rỉ owner, một Grammar tự chế, một lỗi responsive, một lỗi tiếp cận, một trạng thái bất
|
|
52
|
+
lợi chưa giải quyết hay một phương án đảo ngược mạnh hơn về bản chất. Dưới `refine`, các phương án là
|
|
53
|
+
những nước đi ở cấp phần tử bên trong cấu trúc đã duyệt, không bao giờ là một cấu trúc mới. Khi
|
|
54
|
+
phương án duy nhất chết dưới một đòn tấn công, lần chạy dừng với `NO_VIABLE_DIRECTION`; khi nhiều
|
|
55
|
+
phương án cùng sống, `DIRECTION_CHOICE_REQUIRED` chọn cái sống sót qua nhiều đòn nhất, trừ khi người
|
|
56
|
+
dùng giữ quyền chọn.
|
|
57
|
+
|
|
58
|
+
## Ranh giới
|
|
59
|
+
|
|
60
|
+
Context chỉ đọc. Operator chỉ ghi `response/` của nhánh mình: biên nhận quyết định, bản liệt kê
|
|
61
|
+
coverage, các trang phương án đã render và `response.json`. Nó không sửa source sản phẩm hay source
|
|
62
|
+
thẩm quyền, không bịa hành vi nghiệp vụ, backend, kiến trúc, xác thực, lưu trữ hay dữ liệu, không
|
|
63
|
+
publish Grammar dùng chung, không khởi động hay cấu hình lại dịch vụ runtime, và không tuyên bố rằng
|
|
64
|
+
implementation, cổng chất lượng thị giác hay một lượt UAT đã qua.
|
|
65
|
+
|
|
66
|
+
## Hình là thứ operator tự xét, không chờ yêu cầu
|
|
67
|
+
|
|
68
|
+
Một tấm hình là quyết định composition như mọi quyết định khác: khi một ứng viên để lại vùng đọc lên
|
|
69
|
+
thấy trống (hero không có chủ thể, empty state chỉ có một câu, hàng card mà chữ không gánh nổi chiều
|
|
70
|
+
rộng), operator thêm hình làm theo đúng một claim của hướng và ghi lý do vào bảng `## Images`. Nó
|
|
71
|
+
không chờ người nói, và cũng không trang trí: vùng mà chữ và Grammar object đã gánh được thì không
|
|
72
|
+
thêm hình, và hình không bao giờ mã hoá claim mà lời hứa nghiệp vụ không hề nêu. Asset và prompt nằm
|
|
73
|
+
dưới `response/artifacts/images/`; `frontend.source.apply` ghi chúng cùng write set đã khai.
|
|
74
|
+
|
|
75
|
+
## Context
|
|
76
|
+
|
|
77
|
+
| Alias | Bind | Bắt buộc |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| `@grammar/core` | Grammar đã publish như app đang bind resolve ra; các composition mà một hướng được phép ràng | có |
|
|
80
|
+
| `@knowledge/ui/composition` | các khẳng định mà biên nhận phát ra phải thoả, `COVERAGE-1` là khẳng định về toàn bộ biên nhận | có |
|
|
81
|
+
| `@workspaces/fe` | checkout frontend được route, đọc ở head đóng băng; hiện trạng là bằng chứng, không bao giờ là hướng được yêu cầu | có |
|
|
82
|
+
| `@knowledge/grammars/starci` | họ Core được kỳ vọng hiện thực hoá Common ra sao; luật về Grammar, không phải chính Grammar | không |
|
|
83
|
+
| `@worktrees/uat/<flow>/<case>` | quan sát hành vi, UX và UI của lần trước kèm ảnh chụp; bằng chứng và phản chứng, một lần pass cũ không phải thẩm quyền hiện tại | không |
|
|
84
|
+
|
|
85
|
+
## Đầu vào
|
|
86
|
+
|
|
87
|
+
| Kind | Từ đâu | Bắt buộc |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| `business-promise-authority` | `business.decide`; bắt buộc với `new`, và với `reconstruct` khi tập trạng thái đổi | không |
|
|
90
|
+
| `backend-source-application` | `backend.source.apply`; bắt buộc khi một contract dữ liệu đổi | không |
|
|
91
|
+
| `architecture-decision` | `architecture.decide`; bắt buộc khi một ranh giới đổi | không |
|
|
92
|
+
| `frontend-direction-decision` | một lần chạy `frontend.direction.decide` trước trên cùng target, đọc khi chạy lại | không |
|
|
93
|
+
|
|
94
|
+
## Yêu cầu
|
|
95
|
+
|
|
96
|
+
| Field | Kiểu | Mặc định | Hỏi |
|
|
97
|
+
| --- | --- | --- | --- |
|
|
98
|
+
| `target` | id | — | Đúng một route, page, layout, modal, drawer, flow, block hay component mà hướng này nhắm tới |
|
|
99
|
+
| `intent` | choice | modify | create, modify, audit-repair hay reconcile; `create` đi cùng change level `new` và chỉ đi cùng nó |
|
|
100
|
+
| `changeLevel` | choice | — | new, reconstruct hay refine; một lượt audit phải kết thúc bằng bề mặt pass là reconstruct |
|
|
101
|
+
| `ownerCeiling` | choice | surface-and-nested-layouts | surface-only, surface-and-nested-layouts hay ancestor-layouts-authorized |
|
|
102
|
+
| `candidates` | number 1–3 | 1 | Hình thành bao nhiêu hướng; nhiều hơn một chỉ khi muốn so sánh |
|
|
103
|
+
| `preview` | choice | no | yes thì render phương án duy nhất thành một trang xem được |
|
|
104
|
+
| `references` | list | [] | Tham chiếu ngoài do người dùng cấp; nghiên cứu có giới hạn chỉ chạy khi ô này rỗng |
|
|
105
|
+
| `selectionPolicy` | choice | automatic | `automatic`: operator chọn và ghi lý do; `approval-required`: người chọn |
|
|
106
|
+
| `approval` | id | null | Id phương án được duyệt; chỉ bắt buộc dưới `approval-required`, nhập khi chạy lại sau `DIRECTION_CHOICE_REQUIRED` |
|
|
107
|
+
| `resume` | token | null | Token của nhánh bị chặn khi vào lại sau một mã dừng |
|
|
108
|
+
|
|
109
|
+
## Các bước
|
|
110
|
+
|
|
111
|
+
| # | Bước | Tham số | Đọc | Ghi | Dừng với |
|
|
112
|
+
| --- | --- | --- | --- | --- | --- |
|
|
113
|
+
| 1 | Kiểm gate và chạy lại | `resume`, `approval` | `request/request.json`, đầu vào `frontend-direction-decision` khi chạy lại, @workspaces/fe ở head đóng băng | — | `INVALID_INPUT`, `SOURCE_DRIFT`, `NO_PROGRESS` |
|
|
114
|
+
| 2 | Soát lời yêu cầu: route, scope, change level, trần owner | `target`, `intent`, `changeLevel`, `ownerCeiling` | @workspaces/fe ở head đóng băng, @tools/git | — | `ROUTE_UNVERIFIED`, `SCOPE_UNFROZEN`, `CHANGE_LEVEL_AMBIGUOUS`, `OWNER_CEILING_INVALID` |
|
|
115
|
+
| 3 | Bind các đầu vào mà change level đòi | `changeLevel` | đầu vào `business-promise-authority`, `backend-source-application` và `architecture-decision` | — | `BUSINESS_REQUIRED`, `BACKEND_REQUIRED`, `ARCHITECTURE_REQUIRED` |
|
|
116
|
+
| 4 | Quan sát context đang có | — | @workspaces/fe (artifact trực tiếp của target, hoặc host được uỷ quyền và họ sản phẩm khi target chưa tồn tại), @worktrees/uat/<flow>/<case> nếu có | — | `EVIDENCE_MISSING` |
|
|
117
|
+
| 5 | Biên một UI contract và coverage của nó | — | @knowledge/ui/composition, đầu vào `business-promise-authority` nếu có, context đã quan sát | `response/data/coverage.json` | `SCOPE_UNFROZEN` |
|
|
118
|
+
| 6 | Đi tìm tham chiếu ngoài, có giới hạn | `references`, `changeLevel` | @knowledge/ui/composition (khoảng trống mà nghiên cứu phải lấp), @tools/websearch | — | `REFERENCE_EVIDENCE_EXHAUSTED` |
|
|
119
|
+
| 7 | Hình thành các phương án | `candidates` | UI contract vừa biên | — | `NO_VIABLE_DIRECTION` |
|
|
120
|
+
| 8 | Áp bộ lọc Grammar | `ownerCeiling` | @grammar/core (component sở hữu gì và có prop nào), @knowledge/grammars/starci | — | `GRAMMAR_REQUIRED` |
|
|
121
|
+
| 9 | Render bằng chứng quyết định và hình đã tự xét | `candidates`, `preview` | các phương án còn sống, @knowledge/grammars/starci | `response/artifacts/<candidateId>.html`, `response/artifacts/images/<slot>.png`, @tools/visualize, @tools/imagegen | — |
|
|
122
|
+
| 10 | Phản chứng | — | các phương án, đầu vào `business-promise-authority` và `backend-source-application`, `response/data/coverage.json` | — | `NO_VIABLE_DIRECTION` |
|
|
123
|
+
| 11 | Quyết | `selectionPolicy`, `approval` | bảng phản chứng | — | `DIRECTION_CHOICE_REQUIRED` |
|
|
124
|
+
| 12 | Phát | — | mọi thứ ở trên | `response/response.md`, `response/response.json` | — |
|
|
125
|
+
|
|
126
|
+
Bước 9 chỉ render khi có nhiều hơn một phương án được hình thành hoặc `preview` là yes; một phương án
|
|
127
|
+
duy nhất dưới mặc định không sinh trang nào và dựa vào bước 10. Dưới `automatic`,
|
|
128
|
+
`DIRECTION_CHOICE_REQUIRED` chọn phương án sống sót qua nhiều đòn tấn công nhất và ghi lại bảng; dưới
|
|
129
|
+
`approval-required` nó dừng và người dùng quay lại với `approval`. Biên nhận uỷ quyền cho domain kế
|
|
130
|
+
tiếp resolve và triển khai bên trong trần owner đã đóng băng, và không chứng minh gì về việc kết quả
|
|
131
|
+
render ra sao.
|
|
132
|
+
|
|
133
|
+
## Đầu ra
|
|
134
|
+
|
|
135
|
+
| Kind | File | Kiểu | Bắt buộc |
|
|
136
|
+
| --- | --- | --- | --- |
|
|
137
|
+
| `frontend-direction-decision` | `response/response.md` | md | có |
|
|
138
|
+
| `ui-coverage` | `response/data/coverage.json` | data | có |
|
|
139
|
+
| `candidates` | `response/artifacts/<candidateId>.html` | artifact | không |
|
|
140
|
+
| `direction-image` | `response/artifacts/images/<slot>.png` | artifact | không |
|
|
141
|
+
|
|
142
|
+
## Dừng
|
|
143
|
+
|
|
144
|
+
| Code | Xử lý |
|
|
145
|
+
| --- | --- |
|
|
146
|
+
| `INVALID_INPUT` | terminate |
|
|
147
|
+
| `ROUTE_UNVERIFIED` | terminate |
|
|
148
|
+
| `SOURCE_DRIFT` | terminate |
|
|
149
|
+
| `SCOPE_UNFROZEN` | terminate |
|
|
150
|
+
| `CHANGE_LEVEL_AMBIGUOUS` | terminate |
|
|
151
|
+
| `OWNER_CEILING_INVALID` | terminate |
|
|
152
|
+
| `BUSINESS_REQUIRED` | terminate |
|
|
153
|
+
| `BACKEND_REQUIRED` | terminate |
|
|
154
|
+
| `ARCHITECTURE_REQUIRED` | terminate |
|
|
155
|
+
| `GRAMMAR_REQUIRED` | terminate |
|
|
156
|
+
| `EVIDENCE_MISSING` | terminate |
|
|
157
|
+
| `REFERENCE_EVIDENCE_EXHAUSTED` | terminate |
|
|
158
|
+
| `NO_VIABLE_DIRECTION` | terminate |
|
|
159
|
+
| `DIRECTION_CHOICE_REQUIRED` | fallback |
|
|
160
|
+
| `NO_PROGRESS` | terminate |
|
|
161
|
+
|
|
162
|
+
## Kế tiếp
|
|
163
|
+
|
|
164
|
+
| Khi | Operator |
|
|
165
|
+
| --- | --- |
|
|
166
|
+
| hướng đã quyết; mọi hướng đều resolve giá trị trình bày trước khi ghi source | `frontend.presentation.resolve` |
|
|
167
|
+
| một component của họ mà hướng cần chưa được publish, nên một người publish nó rồi chính hướng ấy chạy lại | `frontend.direction.decide` |
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
// Proves validate.mjs on a synthetic session branch: one conforming branch under the defaults
|
|
2
|
+
// (modify, reconstruct, one candidate, automatic), one three-candidate branch that takes the
|
|
3
|
+
// DIRECTION_CHOICE_REQUIRED fallback, one blocked on the same code under approval-required, and one
|
|
4
|
+
// mutation per law, each of which must fail with a line that names the defect.
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { validateDirectionStep } from './validate.mjs';
|
|
10
|
+
|
|
11
|
+
const HEAD = '0f1e2d3c4b5a69788796a5b4c3d2e1f009182736';
|
|
12
|
+
const NAMES = ['one-column', 'split-view', 'stepper'];
|
|
13
|
+
|
|
14
|
+
const coverage = ({ regions = 2, actions = 1 } = {}) => ({
|
|
15
|
+
directionId: 'plan-picker',
|
|
16
|
+
actions: [{ action: 'choose a plan', pointerRoute: 'the plan card', keyboardRoute: 'tab to the card, enter', states: ['pending', 'confirmed'], pendingPaths: [{ path: 'server accepts', settlement: 'the confirmed plan replaces the picker' }] }].slice(0, actions),
|
|
17
|
+
regions: [
|
|
18
|
+
{ region: 'offer', idiomRef: 'idioms#offer-block', compositionRef: '@grammar/core#SurfaceCard' },
|
|
19
|
+
{ region: 'decision', idiomRef: 'idioms#decision-bar', compositionRef: '@grammar/core#SurfaceListCard' },
|
|
20
|
+
].slice(0, regions),
|
|
21
|
+
states: [{ meaning: 'empty', carrier: 'the offer region' }, { meaning: 'pending', carrier: 'the decision button' }],
|
|
22
|
+
responsive: [{ branch: 'narrow', owner: 'the page shell' }],
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
function responseMd({
|
|
26
|
+
intent = 'modify', changeLevel = 'reconstruct', ownerCeiling = 'surface-and-nested-layouts',
|
|
27
|
+
classification = 'dominant', policy = 'automatic', selected = 'one-column', candidates = 1,
|
|
28
|
+
selectedFails = false, rejectAll = true, references = 0, fallbacks = [],
|
|
29
|
+
} = {}) {
|
|
30
|
+
const formed = NAMES.slice(0, candidates);
|
|
31
|
+
const attacks = formed.map((id) => `| content stress | \`${id}\` | ${selectedFails && id === selected ? 'fails' : 'holds'} | the widest plan name still fits at 360px |`);
|
|
32
|
+
const others = rejectAll ? formed.filter((id) => id !== selected).map((id) => `| \`${id}\` | it loses the offer above the fold on the narrow branch |`) : [];
|
|
33
|
+
const refRows = references ? ['| a public plan picker | https://example.com/plans | it settles nothing about entitlement |'] : [];
|
|
34
|
+
return `# frontend-direction-decision — plan-picker
|
|
35
|
+
|
|
36
|
+
## Decision
|
|
37
|
+
|
|
38
|
+
| Field | Value |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| Direction id | \`plan-picker\` |
|
|
41
|
+
| Target | \`/plans\` |
|
|
42
|
+
| Intent | \`${intent}\` |
|
|
43
|
+
| Change level | \`${changeLevel}\` |
|
|
44
|
+
| Owner ceiling | \`${ownerCeiling}\` |
|
|
45
|
+
| Classification | \`${classification}\` |
|
|
46
|
+
| Selection policy | \`${policy}\` |
|
|
47
|
+
| Selected candidate | \`${selected}\` |
|
|
48
|
+
|
|
49
|
+
## Observed
|
|
50
|
+
|
|
51
|
+
| Item | Evidence |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| what the plans route renders today | \`app/plans/page.tsx:1-64@${HEAD}\` |
|
|
54
|
+
|
|
55
|
+
## UI contract
|
|
56
|
+
|
|
57
|
+
| Element | Kind | Responsibility |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| \`offer\` | region | names the offer and its price |
|
|
60
|
+
| \`decision\` | region | carries the choice and its confirmation |
|
|
61
|
+
| choose a plan | action | the actor picks one plan and the surface answers |
|
|
62
|
+
| empty | state | no plan is offered yet |
|
|
63
|
+
| pending | state | the choice is settling |
|
|
64
|
+
| narrow | responsive | the two regions stack below the breakpoint |
|
|
65
|
+
|
|
66
|
+
## Coverage
|
|
67
|
+
|
|
68
|
+
| Concern | Enumerated |
|
|
69
|
+
| --- | --- |
|
|
70
|
+
| Actions | one action with its routes and one settled pending path |
|
|
71
|
+
| Regions | two regions, each with one idiom and one composition |
|
|
72
|
+
| States | two meanings on two carriers |
|
|
73
|
+
| Responsive | one branch owned by the page shell |
|
|
74
|
+
|
|
75
|
+
## References
|
|
76
|
+
|
|
77
|
+
| Source | URL | Limitation |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
${refRows.join('\n')}
|
|
80
|
+
|
|
81
|
+
## Images
|
|
82
|
+
|
|
83
|
+
| Slot | Why | Claim | File |
|
|
84
|
+
| --- | --- | --- | --- |
|
|
85
|
+
|
|
86
|
+
## Falsification
|
|
87
|
+
|
|
88
|
+
| Attack | Candidate | Verdict | Evidence |
|
|
89
|
+
| --- | --- | --- | --- |
|
|
90
|
+
${attacks.join('\n')}
|
|
91
|
+
|
|
92
|
+
## Why not the others
|
|
93
|
+
|
|
94
|
+
| Candidate | Rejected because |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
${others.join('\n')}
|
|
97
|
+
|
|
98
|
+
## Fallbacks taken
|
|
99
|
+
|
|
100
|
+
| Code | Action |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
${fallbacks.map((c) => `| \`${c}\` | the candidate that survived the most attacks was selected |`).join('\n')}
|
|
103
|
+
`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const requestJson = ({ extra = {} } = {}) => ({
|
|
107
|
+
schemaVersion: 9,
|
|
108
|
+
operatorId: 'frontend.direction.decide',
|
|
109
|
+
step: 2,
|
|
110
|
+
parallel: 1,
|
|
111
|
+
sessionId: 's-test',
|
|
112
|
+
contexts: [{ alias: '@workspaces/fe', head: HEAD }, { alias: '@grammar/core', head: null }, { alias: '@knowledge/ui/composition', head: null }],
|
|
113
|
+
requirements: {
|
|
114
|
+
target: '/plans',
|
|
115
|
+
intent: 'modify',
|
|
116
|
+
changeLevel: 'reconstruct',
|
|
117
|
+
ownerCeiling: 'surface-and-nested-layouts',
|
|
118
|
+
candidates: 1,
|
|
119
|
+
preview: 'no',
|
|
120
|
+
references: [],
|
|
121
|
+
selectionPolicy: 'automatic',
|
|
122
|
+
approval: null,
|
|
123
|
+
resume: null,
|
|
124
|
+
...extra,
|
|
125
|
+
},
|
|
126
|
+
inputs: { 'business-promise-authority': 'step-1/parallel-1/response/response.md' },
|
|
127
|
+
resume: null,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const responseJson = ({ status = 'done', stop, fields, fallbacks = [], next = ['frontend.presentation.resolve'] } = {}) => ({
|
|
131
|
+
schemaVersion: 9,
|
|
132
|
+
operatorId: 'frontend.direction.decide',
|
|
133
|
+
step: 2,
|
|
134
|
+
parallel: 1,
|
|
135
|
+
status,
|
|
136
|
+
...(stop ? { stop } : {}),
|
|
137
|
+
fallbacks,
|
|
138
|
+
fields: fields ?? { 'frontend-direction-decision': 'response/response.md', 'ui-coverage': 'response/data/coverage.json' },
|
|
139
|
+
commits: [],
|
|
140
|
+
next,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
function writeBranch(files) {
|
|
144
|
+
const session = mkdtempSync(path.join(tmpdir(), 'fe-direction-session-'));
|
|
145
|
+
const branch = path.join(session, 'step-2', 'parallel-1');
|
|
146
|
+
for (const d of ['request', 'response/data', 'response/artifacts']) mkdirSync(path.join(branch, d), { recursive: true });
|
|
147
|
+
mkdirSync(path.join(session, 'step-1', 'parallel-1', 'response'), { recursive: true });
|
|
148
|
+
writeFileSync(path.join(session, 'step-1', 'parallel-1', 'response', 'response.md'), '# business-promise-authority — plans\n');
|
|
149
|
+
writeFileSync(path.join(session, 'state.json'), JSON.stringify({ id: 's-test', chain: [['1/1'], ['2/1']], steps: { '1/1': 'business.decide', '2/1': 'frontend.direction.decide' }, current: '2/1', status: 'running' }));
|
|
150
|
+
for (const [name, content] of Object.entries(files)) {
|
|
151
|
+
if (content === null) continue;
|
|
152
|
+
writeFileSync(path.join(branch, name), typeof content === 'string' ? content : JSON.stringify(content, null, 2));
|
|
153
|
+
}
|
|
154
|
+
return { branch, session };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const baseline = () => ({
|
|
158
|
+
'request/request.json': requestJson(),
|
|
159
|
+
'response/response.json': responseJson(),
|
|
160
|
+
'response/response.md': responseMd(),
|
|
161
|
+
'response/data/coverage.json': coverage(),
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const threeCandidates = ({ policy = 'automatic', approval = null, extraResponse = {}, md = {} } = {}) => ({
|
|
165
|
+
'request/request.json': requestJson({ extra: { candidates: 3, selectionPolicy: policy, approval } }),
|
|
166
|
+
'response/response.json': responseJson({
|
|
167
|
+
fallbacks: policy === 'automatic' ? ['DIRECTION_CHOICE_REQUIRED'] : [],
|
|
168
|
+
fields: { 'frontend-direction-decision': 'response/response.md', 'ui-coverage': 'response/data/coverage.json', candidates: NAMES.map((n) => `response/artifacts/${n}.html`) },
|
|
169
|
+
...extraResponse,
|
|
170
|
+
}),
|
|
171
|
+
'response/response.md': responseMd({ policy, candidates: 3, fallbacks: policy === 'automatic' ? ['DIRECTION_CHOICE_REQUIRED'] : [], ...md }),
|
|
172
|
+
'response/data/coverage.json': coverage(),
|
|
173
|
+
...Object.fromEntries(NAMES.map((n) => [`response/artifacts/${n}.html`, `<!doctype html><title>${n}</title>`])),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
async function expectValid(files, label) {
|
|
177
|
+
const { branch, session } = writeBranch(files);
|
|
178
|
+
const { errors } = await validateDirectionStep(branch);
|
|
179
|
+
rmSync(session, { recursive: true, force: true });
|
|
180
|
+
assert.deepEqual(errors, [], `${label} should be valid`);
|
|
181
|
+
}
|
|
182
|
+
async function expectError(files, needle, label) {
|
|
183
|
+
const { branch, session } = writeBranch(files);
|
|
184
|
+
const { errors } = await validateDirectionStep(branch);
|
|
185
|
+
rmSync(session, { recursive: true, force: true });
|
|
186
|
+
assert.ok(errors.some((e) => e.includes(needle)), `${label}: expected an error containing "${needle}", got:\n${errors.join('\n') || '(none)'}`);
|
|
187
|
+
}
|
|
188
|
+
const withCoverage = (change) => { const c = coverage(); change(c); return { ...baseline(), 'response/data/coverage.json': c }; };
|
|
189
|
+
|
|
190
|
+
await expectValid(baseline(), 'defaults (modify, reconstruct, one candidate, automatic)');
|
|
191
|
+
await expectValid(threeCandidates(), 'three candidates, the choice taken as a fallback');
|
|
192
|
+
await expectValid(threeCandidates({ policy: 'approval-required', approval: 'one-column' }), 'three candidates approved by the person');
|
|
193
|
+
await expectValid({
|
|
194
|
+
'request/request.json': requestJson({ extra: { candidates: 3, selectionPolicy: 'approval-required' } }),
|
|
195
|
+
'response/response.json': responseJson({ status: 'blocked', stop: 'DIRECTION_CHOICE_REQUIRED', next: [], fields: { candidates: NAMES.map((n) => `response/artifacts/${n}.html`) } }),
|
|
196
|
+
...Object.fromEntries(NAMES.map((n) => [`response/artifacts/${n}.html`, `<!doctype html><title>${n}</title>`])),
|
|
197
|
+
}, 'DIRECTION_CHOICE_REQUIRED terminates under approval-required');
|
|
198
|
+
|
|
199
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { intent: 'create' } }) }, 'intent create requires changeLevel new', 'create without new');
|
|
200
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { candidates: 4 } }) }, 'candidates must be 1, 2 or 3', 'four candidates');
|
|
201
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { approval: 'one-column' } }) }, 'approval is bound under automatic policy', 'approval under automatic');
|
|
202
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { mystery: 1 } }) }, 'requirements.mystery is not a field', 'undeclared requirement');
|
|
203
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { changeLevel: '' } }) }, 'required field changeLevel has no value', 'missing change level');
|
|
204
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { candidates: 2 } }) }, 'more than one candidate was formed but none was rendered', 'comparison without pages');
|
|
205
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { preview: 'yes' } }) }, 'preview was asked for but no candidate page was rendered', 'preview without a page');
|
|
206
|
+
await expectError({ ...baseline(), 'response/response.json': responseJson({ fields: { 'frontend-direction-decision': 'response/response.md', 'ui-coverage': 'response/data/coverage.json', candidates: 'response/artifacts/one-column.html' } }), 'response/artifacts/one-column.html': '<!doctype html>' }, 'one candidate under no preview renders no page', 'unasked preview page');
|
|
207
|
+
await expectError({ ...baseline(), 'response/response.md': responseMd({ changeLevel: 'refine' }) }, 'Change level refine differs from the request', 'receipt and request disagree on the change level');
|
|
208
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { changeLevel: 'refine' } }), 'response/response.md': responseMd({ changeLevel: 'refine' }) }, 'a refine is classified locked-refine', 'refine classified dominant');
|
|
209
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { changeLevel: 'refine' } }), 'response/response.md': responseMd({ changeLevel: 'refine', classification: 'locked-refine', references: 1 }) }, 'a refine works from the family idioms alone', 'refine that researched');
|
|
210
|
+
await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { references: ['https://example.com/plans'] } }) }, 'the person supplied references and none of them is recorded', 'supplied reference dropped');
|
|
211
|
+
await expectError({ ...baseline(), 'response/response.md': responseMd({ selectedFails: true }) }, 'fails an attack, so the direction is not decided', 'selected candidate fails an attack');
|
|
212
|
+
await expectError({ ...threeCandidates(), 'response/response.md': responseMd({ candidates: 3, rejectAll: false, fallbacks: ['DIRECTION_CHOICE_REQUIRED'] }) }, 'is not rejected by name', 'unselected candidate not rejected');
|
|
213
|
+
await expectError({ ...baseline(), 'response/response.md': responseMd({ candidates: 2 }).replace('| `split-view` | it loses', '| `absent` | it loses') }, 'Falsification covers 2 candidates, the request asked for 1', 'more candidates than asked');
|
|
214
|
+
await expectError({ ...threeCandidates({ policy: 'approval-required', approval: 'split-view' }) }, 'but approval names split-view', 'approval names another candidate');
|
|
215
|
+
await expectError({ ...threeCandidates({ policy: 'approval-required' }) }, 'approval-required with no approval cannot end done', 'approval-required decided without an approval');
|
|
216
|
+
await expectError(withCoverage((c) => { c.regions = c.regions.slice(0, 1); }), 'COVERAGE-1: region is not covered: decision', 'region uncovered');
|
|
217
|
+
await expectError(withCoverage((c) => { c.actions = []; }), 'COVERAGE-1: actions must enumerate every declared action', 'actions uncovered');
|
|
218
|
+
await expectError(withCoverage((c) => { c.states[1].carrier = 'the offer region'; }), 'COVERAGE-1: two meanings share one carrier', 'two meanings on one carrier');
|
|
219
|
+
await expectError(withCoverage((c) => { c.actions[0].pendingPaths[0].settlement = ''; }), 'pending path without a settlement', 'unsettled pending path');
|
|
220
|
+
await expectError(withCoverage((c) => { c.directionId = 'other-picker'; }), 'differs from the receipt', 'coverage names another direction');
|
|
221
|
+
await expectError({ ...baseline(), 'response/data/coverage.json': null, 'response/response.json': responseJson({ fields: { 'frontend-direction-decision': 'response/response.md' } }) }, 'required output ui-coverage is not in fields', 'missing required output');
|
|
222
|
+
await expectError({ ...baseline(), 'response/response.md': responseMd().replace('## Observed', '## Observations') }, 'missing section ^## Observed$', 'receipt section renamed');
|
|
223
|
+
await expectError({ ...baseline(), 'response/response.json': responseJson({ status: 'blocked', stop: 'DIRECTION_CHOICE_REQUIRED', next: [] }) }, 'has disposition fallback under these requirements', 'terminating on the choice under automatic');
|
|
224
|
+
await expectError({ ...baseline(), 'response/response.json': responseJson({ status: 'blocked', stop: 'MADE_UP_CODE', next: [] }) }, 'not a registered code', 'unknown stop code');
|
|
225
|
+
|
|
226
|
+
process.stdout.write('frontend.direction.decide self-test: 4 valid branches, 24 rejected mutations\n');
|