aw-spec-cli 0.1.33 → 0.1.35
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/README.md +76 -81
- package/assets/.appweaver/DESIGN-FORMAT.md +57 -25
- package/assets/.appweaver/STACK-PROFILES.md +95 -60
- package/assets/.appweaver/orchestrator/LAUNCHER.md +1 -1
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +173 -32
- package/assets/.appweaver/orchestrator/modules/domain-select.orch.yaml +29 -1
- package/assets/.appweaver/orchestrator/modules/foundation-pack.orch.yaml +7 -4
- package/assets/.appweaver/orchestrator/modules/foundation-spec.orch.yaml +49 -60
- package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +58 -0
- package/assets/.appweaver/orchestrator/orchestrator.py +26 -8
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +20 -20
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +22 -48
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +9 -7
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +5 -5
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +239 -40
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +254 -43
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +248 -68
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +251 -20
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +316 -10
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +6 -47
- package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +62 -16
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +132 -9
- package/assets/.appweaver/orchestrator/workflows/aw-spec-fix-test-coverage.orch.yaml +965 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +222 -169
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +21 -22
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +453 -120
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-lane.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +83 -6
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +45 -2
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +324 -74
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +275 -44
- package/assets/.appweaver/scripts/python/aw-foundation.py +16 -13
- package/assets/.appweaver/scripts/python/aw-gaps.py +11 -3
- package/assets/.appweaver/scripts/python/aw-model-guard.py +295 -0
- package/assets/.appweaver/scripts/python/aw-plan-coverage.py +412 -3
- package/assets/.appweaver/scripts/python/aw-plangate.py +23 -16
- package/assets/.appweaver/scripts/python/aw-stack.py +1794 -265
- package/assets/.appweaver/stack-profiles/dotnet-webapi.json +0 -2
- package/assets/.appweaver/stack-profiles/java-spring.json +0 -2
- package/assets/.appweaver/templates/api-plan-template.md +107 -0
- package/assets/.appweaver/templates/delta-api-plan-template.md +87 -0
- package/assets/.appweaver/templates/delta-design-template.md +25 -23
- package/assets/.appweaver/templates/delta-spec-template.md +3 -3
- package/assets/.appweaver/templates/design-template.md +20 -42
- package/assets/.appweaver/templates/domain-model-template.md +3 -0
- package/assets/.appweaver/templates/ui-delta-design-template.md +11 -8
- package/assets/.appweaver/templates/ui-design-template.md +4 -4
- package/assets/foundations/foundation.spec.template.txt +25 -18
- package/assets/guardrails/README.md +61 -25
- package/assets/guardrails/methodology/ux/accessibility-standards.md +38 -0
- package/assets/guardrails/methodology/ux/content-design.md +34 -0
- package/assets/guardrails/methodology/ux/interaction-states.md +45 -0
- package/assets/guardrails/methodology/ux/journey-mapping.md +41 -0
- package/assets/guardrails/project-specific/domain/domain-requirements.md +25 -38
- package/assets/guardrails/project-specific/domain-rule-bindings.md +33 -69
- package/assets/guardrails/project-specific/frontend/ui-requirements.md +2 -2
- package/assets/guardrails/project-specific/service/technical-requirements.md +2 -2
- package/assets/guardrails/project-specific/tech-rule-bindings.md +16 -13
- package/assets/guardrails/sample-healthcare/domain/domain-requirements.md +58 -0
- package/assets/guardrails/sample-healthcare/domain-rule-bindings.md +138 -0
- package/assets/guardrails/sample-healthcare/frontend/ui-requirements.md +43 -0
- package/assets/guardrails/sample-healthcare/service/technical-requirements.md +40 -0
- package/assets/guardrails/sample-healthcare/tech-rule-bindings.md +303 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +216 -7
- package/dist/commands/init.js.map +1 -1
- package/dist/utils/config.js +1 -1
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/foundation.d.ts +36 -0
- package/dist/utils/foundation.d.ts.map +1 -0
- package/dist/utils/foundation.js +109 -0
- package/dist/utils/foundation.js.map +1 -0
- package/dist/utils/paths.js +1 -1
- package/dist/utils/paths.js.map +1 -1
- package/package.json +1 -1
|
@@ -21,6 +21,15 @@
|
|
|
21
21
|
# (referred to directly, as-is — NOT mapped) plus the caller's minimal seed, so an
|
|
22
22
|
# unconfigured / greenfield project is never blocked. This keeps greenfield-safety.
|
|
23
23
|
#
|
|
24
|
+
# SOFT, NOT SILENT. Where a map exists but loads less than it says, the module WARNS
|
|
25
|
+
# and carries on: the map has no `## /{{command}}` heading (absent or misspelled — a
|
|
26
|
+
# heading that is present and deliberately empty does not warn), a mapped file is not on
|
|
27
|
+
# disk, or a bullet uses a placeholder the kit does not know. The engine returns each
|
|
28
|
+
# warning in the run result, so the operator sees it even though the run continues.
|
|
29
|
+
# A stack with NO rule folder under guardrails/technology/ is reported once, as that
|
|
30
|
+
# folder: it is a supported setup (STACK-PROFILES.md §7) that select-stack already
|
|
31
|
+
# reported, not a list of typos. Files missing from a folder that exists are listed.
|
|
32
|
+
#
|
|
24
33
|
# Runs in the CALLER's context: shares vars + standards + the caller's `scripts:`
|
|
25
34
|
# map + replay cache (include semantics). HITL steps suspend via the checkpoint
|
|
26
35
|
# channel under every strategy; non-interactive default: selection → all.
|
|
@@ -52,11 +61,60 @@ workflow:
|
|
|
52
61
|
outputs:
|
|
53
62
|
- { name: tech_domain, from: "$.domain" }
|
|
54
63
|
- { name: tg_techmap_exists, from: "$.techrulemap_exists" }
|
|
64
|
+
- { name: tg_heading_found, from: "$.heading_found" }
|
|
55
65
|
- { name: tg_candidates, from: "$.candidates" }
|
|
66
|
+
- { name: tg_missing, from: "$.missing_elsewhere" }
|
|
67
|
+
- { name: tg_absent, from: "$.absent_corpora" }
|
|
68
|
+
- { name: tg_absent_text, from: "$.absent_corpora_text" }
|
|
69
|
+
- { name: tg_absent_files, from: "$.absent_files" }
|
|
70
|
+
- { name: tg_unknown, from: "$.unknown_placeholders" }
|
|
56
71
|
- { name: tg_mismatch, from: "$.stack_mismatch" }
|
|
57
72
|
- { name: tg_mismatched, from: "$.mismatched_stacks" }
|
|
58
73
|
- { name: tg_declared_stacks, from: "$.declared_stacks" }
|
|
59
74
|
|
|
75
|
+
# 1b. WHAT THE MAP SAYS AND DOES NOT LOAD — warnings, never stops. Each fires only where
|
|
76
|
+
# the map exists; with no map at all there is nothing to misread. The engine has no
|
|
77
|
+
# "list is not empty" test, so each list sets a flag false when it has entries.
|
|
78
|
+
- id: tg_flags
|
|
79
|
+
action: set
|
|
80
|
+
vars: { tg_all_on_disk: "true", tg_placeholders_known: "true", tg_corpora_present: "true" }
|
|
81
|
+
- id: tg_flag_missing
|
|
82
|
+
condition: "{{tg_missing}} != []"
|
|
83
|
+
action: set
|
|
84
|
+
vars: { tg_all_on_disk: "false" }
|
|
85
|
+
- id: tg_flag_absent
|
|
86
|
+
condition: "{{tg_absent}} != []"
|
|
87
|
+
action: set
|
|
88
|
+
vars: { tg_corpora_present: "false" }
|
|
89
|
+
- id: tg_flag_unknown
|
|
90
|
+
condition: "{{tg_unknown}} != []"
|
|
91
|
+
action: set
|
|
92
|
+
vars: { tg_placeholders_known: "false" }
|
|
93
|
+
- id: warn_no_heading
|
|
94
|
+
condition: "{{tg_techmap_exists}} == true"
|
|
95
|
+
action: verify_context
|
|
96
|
+
require_true: tg_heading_found
|
|
97
|
+
on_false: continue
|
|
98
|
+
on_false_message: "WARNING — guardrails/{{tech_domain}}/tech-rule-bindings.md has no `## /{{command}}` heading, so this step loads no technical rules from the map. If that is not intended, add the heading and its rule files — it must read exactly `## /{{command}}`."
|
|
99
|
+
- id: warn_absent_corpus
|
|
100
|
+
condition: "{{tg_techmap_exists}} == true"
|
|
101
|
+
action: verify_context
|
|
102
|
+
require_true: tg_corpora_present
|
|
103
|
+
on_false: continue
|
|
104
|
+
on_false_message: "WARNING — no technical rule set for {{tg_absent_text}} (the folder is not there), so the {{tg_absent_files}} file(s) guardrails/{{tech_domain}}/tech-rule-bindings.md maps there under `## /{{command}}` were not loaded. A stack without a rule set is supported: this step works from ai-rules.md and the technical constitution. To give the lane stack rules, write that folder with the file names the map lists, or point the map at your own files."
|
|
105
|
+
- id: warn_missing
|
|
106
|
+
condition: "{{tg_techmap_exists}} == true"
|
|
107
|
+
action: verify_context
|
|
108
|
+
require_true: tg_all_on_disk
|
|
109
|
+
on_false: continue
|
|
110
|
+
on_false_message: "WARNING — guardrails/{{tech_domain}}/tech-rule-bindings.md maps these files under `## /{{command}}`, but they are not on disk, so they were not loaded: {{tg_missing}}. Correct the paths, or add the files."
|
|
111
|
+
- id: warn_unknown_placeholder
|
|
112
|
+
condition: "{{tg_techmap_exists}} == true"
|
|
113
|
+
action: verify_context
|
|
114
|
+
require_true: tg_placeholders_known
|
|
115
|
+
on_false: continue
|
|
116
|
+
on_false_message: "WARNING — these bullets under `## /{{command}}` in guardrails/{{tech_domain}}/tech-rule-bindings.md use a placeholder the kit does not know, so they were not loaded: {{tg_unknown}}. The known placeholders are backend_stack, frontend_stack, stack, backend_ecosystem and frontend_ecosystem, each in double braces."
|
|
117
|
+
|
|
60
118
|
# 1a. STACK MISMATCH — the map points at a technical corpus for a stack this repo
|
|
61
119
|
# does not declare. These files become STICKY STANDARDS: the generator obeys
|
|
62
120
|
# them for the rest of the run, so a Spring repo mapped to the .NET corpus is
|
|
@@ -240,6 +240,11 @@ class Orchestrator:
|
|
|
240
240
|
# paths an agent_generate reply named as written that were not on disk —
|
|
241
241
|
# run-cumulative, reported at the end beside missing_required.
|
|
242
242
|
self.reported_missing = []
|
|
243
|
+
# the messages of gates that failed and let the run CONTINUE — distinct, in
|
|
244
|
+
# order, returned in the run result. Under --json the run log is not printed,
|
|
245
|
+
# so this list is the only way a warning reaches the operator. A resumed run
|
|
246
|
+
# replays from the top, which rebuilds the list rather than doubling it.
|
|
247
|
+
self.warnings = []
|
|
243
248
|
# agent_author targets a --dry-run left unwritten; required_files gates
|
|
244
249
|
# count them as present, so a dry run reaches the steps after them.
|
|
245
250
|
self.dry_run_unwritten = []
|
|
@@ -1049,23 +1054,29 @@ class Orchestrator:
|
|
|
1049
1054
|
self.emit(f"[execute_script] -> {json.dumps(data)[:200]}")
|
|
1050
1055
|
|
|
1051
1056
|
# -- F2: gates ---------------------------------------------------------------
|
|
1052
|
-
def _gate_failed(self, p, outcome, msg):
|
|
1057
|
+
def _gate_failed(self, p, outcome, msg, explicit=True):
|
|
1053
1058
|
"""Apply a failed gate's outcome: `stop` ends the skill, `return` ends the module
|
|
1054
1059
|
being included (see Return), anything else continues. `message_var` binds the
|
|
1055
1060
|
resolved message first, so a caller that continues or returns can still report
|
|
1056
|
-
why — a message that only reaches the run log is not seen by the operator.
|
|
1061
|
+
why — a message that only reaches the run log is not seen by the operator.
|
|
1062
|
+
A gate that continues with a message of its own (`explicit`) and no
|
|
1063
|
+
`message_var` adds it to the run's `warnings`; a gate with `message_var`
|
|
1064
|
+
reports through the variable instead."""
|
|
1057
1065
|
if p.get("message_var"):
|
|
1058
1066
|
self.vars[p["message_var"]] = msg
|
|
1059
1067
|
if outcome == "stop":
|
|
1060
1068
|
raise Stop(msg)
|
|
1061
1069
|
if outcome == "return":
|
|
1062
1070
|
raise Return(msg)
|
|
1071
|
+
if explicit and not p.get("message_var") and msg not in self.warnings:
|
|
1072
|
+
self.warnings.append(msg)
|
|
1063
1073
|
|
|
1064
1074
|
def act_verify_context(self, step):
|
|
1065
1075
|
"""F2 — the four decl gate forms: required_files (+allow_missing),
|
|
1066
1076
|
require_non_empty (+on_empty), require_true (+on_false),
|
|
1067
1077
|
require_value/equals (+on_mismatch). Messages interpolate {{vars}}.
|
|
1068
|
-
Outcomes: stop | return | continue; `message_var` binds a failed gate's message
|
|
1078
|
+
Outcomes: stop | return | continue; `message_var` binds a failed gate's message,
|
|
1079
|
+
and a continued gate's own message is otherwise collected into `warnings`."""
|
|
1069
1080
|
p = step.get("parameters") or step
|
|
1070
1081
|
if p.get("require_non_empty"):
|
|
1071
1082
|
var = p["require_non_empty"]
|
|
@@ -1073,7 +1084,7 @@ class Orchestrator:
|
|
|
1073
1084
|
if not val:
|
|
1074
1085
|
msg = self.resolve(p.get("on_empty_message", f"{var} is empty"))
|
|
1075
1086
|
self.emit(f"[verify_context] {var} is empty: {msg}")
|
|
1076
|
-
self._gate_failed(p, p.get("on_empty"), msg)
|
|
1087
|
+
self._gate_failed(p, p.get("on_empty"), msg, "on_empty_message" in p)
|
|
1077
1088
|
else:
|
|
1078
1089
|
self.emit(f"[verify_context] {var} non-empty")
|
|
1079
1090
|
if p.get("require_true"):
|
|
@@ -1084,7 +1095,7 @@ class Orchestrator:
|
|
|
1084
1095
|
if not truthy:
|
|
1085
1096
|
msg = self.resolve(p.get("on_false_message", f"{var} is not satisfied"))
|
|
1086
1097
|
self.emit(f"[verify_context] gate failed ({var}): {msg}")
|
|
1087
|
-
self._gate_failed(p, p.get("on_false", "stop"), msg)
|
|
1098
|
+
self._gate_failed(p, p.get("on_false", "stop"), msg, "on_false_message" in p)
|
|
1088
1099
|
else:
|
|
1089
1100
|
self.emit(f"[verify_context] gate ok ({var})")
|
|
1090
1101
|
if p.get("require_value"):
|
|
@@ -1094,7 +1105,7 @@ class Orchestrator:
|
|
|
1094
1105
|
if actual != expected:
|
|
1095
1106
|
msg = self.resolve(p.get("on_mismatch_message", f"{var} != {expected}"))
|
|
1096
1107
|
self.emit(f"[verify_context] value gate failed ({var}={actual!r}): {msg}")
|
|
1097
|
-
self._gate_failed(p, p.get("on_mismatch", "stop"), msg)
|
|
1108
|
+
self._gate_failed(p, p.get("on_mismatch", "stop"), msg, "on_mismatch_message" in p)
|
|
1098
1109
|
else:
|
|
1099
1110
|
self.emit(f"[verify_context] value gate ok ({var}=={expected})")
|
|
1100
1111
|
for rf in p.get("required_files", []) or []:
|
|
@@ -1585,7 +1596,8 @@ class Orchestrator:
|
|
|
1585
1596
|
self.emit(f"[return] workflow ended early: {str(r)[:200]}")
|
|
1586
1597
|
return {"status": "ok", "outputs": self.resolve(self.wf.get("outputs", {}) or {}),
|
|
1587
1598
|
"vars": self.vars, "missing_required": list(self.missing_required),
|
|
1588
|
-
"reported_not_written": list(self.reported_missing)
|
|
1599
|
+
"reported_not_written": list(self.reported_missing),
|
|
1600
|
+
"warnings": list(self.warnings)}
|
|
1589
1601
|
|
|
1590
1602
|
|
|
1591
1603
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -2086,7 +2098,8 @@ def main(argv=None):
|
|
|
2086
2098
|
except Stop as e:
|
|
2087
2099
|
result = {"status": "stopped", "reason": str(e), "vars": orch.vars,
|
|
2088
2100
|
"missing_required": list(orch.missing_required),
|
|
2089
|
-
"reported_not_written": list(orch.reported_missing)
|
|
2101
|
+
"reported_not_written": list(orch.reported_missing),
|
|
2102
|
+
"warnings": list(orch.warnings)}
|
|
2090
2103
|
_clear_checkpoint(opts)
|
|
2091
2104
|
|
|
2092
2105
|
if opts.json:
|
|
@@ -2110,6 +2123,11 @@ def main(argv=None):
|
|
|
2110
2123
|
"later step read them:")
|
|
2111
2124
|
for m in result["reported_not_written"]:
|
|
2112
2125
|
print(f" - {m}")
|
|
2126
|
+
if result.get("warnings"):
|
|
2127
|
+
print(f"\nWARNING: {len(result['warnings'])} warning(s) from gates that let "
|
|
2128
|
+
"the run continue:")
|
|
2129
|
+
for w in result["warnings"]:
|
|
2130
|
+
print(f" - {w}")
|
|
2113
2131
|
if result.get("outputs"):
|
|
2114
2132
|
print(json.dumps(result["outputs"], indent=2))
|
|
2115
2133
|
return 0 if result["status"] == "ok" else 1
|
|
@@ -26,7 +26,11 @@
|
|
|
26
26
|
• ONE MASTER PER LANE — a change is planned against
|
|
27
27
|
specs/<Context>/<Feature>/backend-plan.md; it never opens a second plan
|
|
28
28
|
document and never edits the master. Neither does finalize-delta: the
|
|
29
|
-
master is re-derived from the merged spec and
|
|
29
|
+
master is re-derived from the merged spec, model and API plan by `plan main`.
|
|
30
|
+
• THE CHANGE'S API IS NOT PLANNED HERE — its endpoints and the requests it
|
|
31
|
+
answers are in delta-api-plan.md beside this file, from the kit's
|
|
32
|
+
delta-api-plan-template, the same in every project. This document plans
|
|
33
|
+
what realises them.
|
|
30
34
|
• ADDED / MODIFIED / REMOVED — the complete classification. A shared element
|
|
31
35
|
is MODIFIED in place, never duplicated into the change.
|
|
32
36
|
• EVERY ROW SERVES A SPEC ID — the delta reads the delta-spec and the domain
|
|
@@ -53,13 +57,15 @@
|
|
|
53
57
|
|
|
54
58
|
**Model delta**: `specs/[Context]/domain-model.[CR-ID].delta.md` <!-- "none" when behaviour-only -->
|
|
55
59
|
|
|
60
|
+
**API delta**: `changes/[CR-ID]/delta-api-plan.md` <!-- the endpoints this change adds, changes or removes, which this plan realises -->
|
|
61
|
+
|
|
56
62
|
<!--
|
|
57
63
|
This delta applies to the SINGLE master plan at
|
|
58
64
|
specs/[Context]/[Feature]/backend-plan.md — never a new plan doc. It does not
|
|
59
65
|
touch the master, and neither does finalize-delta: the master is DERIVED from
|
|
60
|
-
spec.md
|
|
61
|
-
`plan main` re-derives it rather than patching it from this file.
|
|
62
|
-
archives this document as the record of how the change was built.
|
|
66
|
+
spec.md, domain-model.md and api-plan.md, the documents finalize-delta merges,
|
|
67
|
+
so `plan main` re-derives it rather than patching it from this file.
|
|
68
|
+
close-delta archives this document as the record of how the change was built.
|
|
63
69
|
|
|
64
70
|
IT PLANS AGAINST THE EFFECTIVE MODEL — the master domain-model.md, this change's
|
|
65
71
|
own model delta, and the PREDECESSOR deltas (the open ones in a LOWER wave,
|
|
@@ -124,21 +130,14 @@
|
|
|
124
130
|
[Which of the master's sections ripple; shared elements touched; whether a data
|
|
125
131
|
migration is needed (yes/no); any contract change other systems couple to.]
|
|
126
132
|
|
|
127
|
-
##
|
|
133
|
+
## API changes
|
|
128
134
|
|
|
129
|
-
<!--
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
API document; an endpoint that already has one keeps it. A `changed` or
|
|
134
|
-
`removed` row whose operation the frontend plan binds to is reported by
|
|
135
|
-
analyze-plan-delta (contract-impact reads the Operation id and Change
|
|
136
|
-
columns) until the frontend delta of this change — or a follow-up change —
|
|
137
|
-
carries the matching change. -->
|
|
135
|
+
<!-- The endpoints this change adds, changes or removes — its Contract deltas table and
|
|
136
|
+
their blocks — and the frontend lane's requests it answers are in delta-api-plan.md,
|
|
137
|
+
beside this file, not here. Keep this section as the one line below; omit it where the
|
|
138
|
+
code publishes no endpoint. -->
|
|
138
139
|
|
|
139
|
-
|
|
140
|
-
|---|---|---|---|---|
|
|
141
|
-
| `[OperationId]` | `[contract]` | [added / changed / removed] | [request → response field shapes] | IC-[nnn] |
|
|
140
|
+
The endpoints this change adds, changes or removes, and the requests it answers, are in [delta-api-plan.md](delta-api-plan.md).
|
|
142
141
|
|
|
143
142
|
## Other open deltas
|
|
144
143
|
|
|
@@ -164,8 +163,9 @@ changed.]
|
|
|
164
163
|
## On merge
|
|
165
164
|
|
|
166
165
|
`finalize-delta` merges the change's SPEC half into `spec.md` and its MODEL half into the
|
|
167
|
-
context's `domain-model.md`, in one transaction,
|
|
168
|
-
|
|
169
|
-
|
|
166
|
+
context's `domain-model.md`, in one transaction, merges its `delta-api-plan.md` into the
|
|
167
|
+
feature's `api-plan.md` by script, and leaves the change OPEN. It does NOT merge this
|
|
168
|
+
document: the master plan is derived from those three, so `plan main` re-derives it rather
|
|
169
|
+
than patching it. `close-delta` archives this file with the change, bumps the
|
|
170
170
|
minor version and writes the Change History rows. None of it is this document's job — it
|
|
171
171
|
records the change, it does not apply it.
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
• Exactly one §2 subsection's heading contains the word "Integration".
|
|
31
31
|
`aw-xref` matches it by keyword to extract contract edges, which is what
|
|
32
32
|
lets a derived layout and the greenfield layout be read by one tool.
|
|
33
|
-
•
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
`contract-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
• The published endpoints are NOT in this template. Each feature keeps them
|
|
34
|
+
in its api-plan.md, from the kit's api-plan-template — one form for every
|
|
35
|
+
project, because the contract scripts read it (`contract-draft` drafts
|
|
36
|
+
api-draft.json from it, `contract-requests` reads its operation ids and
|
|
37
|
+
requests table, `contract-merge` merges a change into it). The §2 axis
|
|
38
|
+
that publishes them keeps the project's own name and its component table,
|
|
39
|
+
and ends with the one line pointing to api-plan.md.
|
|
40
40
|
• The traceability spine (§4) and both ledgers (§5 DA-n, §6 WA-n) survive
|
|
41
41
|
every layout — the analyze gates read them.
|
|
42
42
|
• The §0–§6 numbering itself: `plan`'s bootstrap_bf branch names those
|
|
@@ -69,6 +69,8 @@
|
|
|
69
69
|
|
|
70
70
|
**Source model**: `specs/[Context]/domain-model.md` <!-- where the context has one -->
|
|
71
71
|
|
|
72
|
+
**API plan**: `specs/[Context]/[Feature]/api-plan.md` <!-- the endpoints this service publishes, which §2 realises -->
|
|
73
|
+
|
|
72
74
|
**Version**: v1.0
|
|
73
75
|
|
|
74
76
|
**Status**: Draft
|
|
@@ -176,8 +178,9 @@
|
|
|
176
178
|
the titles are the code's answer — the slots below are illustrative
|
|
177
179
|
positions, not a required set. In each: the components that live there,
|
|
178
180
|
their behaviour in signature form, and the contracts they expose or
|
|
179
|
-
require. The endpoints the service PUBLISHES
|
|
180
|
-
|
|
181
|
+
require. The endpoints the service PUBLISHES are in the feature's
|
|
182
|
+
api-plan.md; the axis below that publishes them ends with the line
|
|
183
|
+
pointing there. -->
|
|
181
184
|
|
|
182
185
|
### 2.1 [PROJECT_STRUCTURAL_AXIS_NAME]
|
|
183
186
|
|
|
@@ -194,47 +197,18 @@
|
|
|
194
197
|
|
|
195
198
|
### 2.[M] [AXIS_THAT_PUBLISHES_THE_ENDPOINTS]
|
|
196
199
|
|
|
197
|
-
<!--
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
`| Request | Outcome |` table (contract-requests). A project that publishes no
|
|
204
|
-
endpoint omits both. -->
|
|
200
|
+
<!-- The axis this project publishes its endpoints from keeps the project's own
|
|
201
|
+
name and its component table. The endpoints themselves — route, verb,
|
|
202
|
+
operation id, shapes, the error contract — and the answers to the frontend
|
|
203
|
+
lane's requests are in the feature's api-plan.md, in the kit's fixed form the
|
|
204
|
+
contract scripts read, and each maps to a component here. End the axis with
|
|
205
|
+
the line below; a project that publishes no endpoint omits it. -->
|
|
205
206
|
|
|
206
207
|
| Component | Behaviour (signature) | Pre / Post / Raises | Serves |
|
|
207
208
|
|---|---|---|---|
|
|
208
209
|
| `[Component]` | `[operation](args) -> result` | [pre / post / raises] | FR-00X · US-Y |
|
|
209
210
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
- **Operation id**: `[OperationId]` <!-- the endpoint's name in the published API document. An endpoint the code already serves keeps the id it publishes, spelled exactly as the code spells it; where it publishes none, write `—` and record a §5 DA-n saying so — never an invented id, which the service's exported document would not carry. A NEW endpoint takes an id unique across the service, and implement gives it exactly that. -->
|
|
213
|
-
- **Auth**: [required role/scope, as enforced]
|
|
214
|
-
- **Request**: `[path/query params]` + body `{ [field]: [type], … }`
|
|
215
|
-
- **Success**: `2xx` → `{ [response shape] }`
|
|
216
|
-
- **Errors** *(the error contract)*:
|
|
217
|
-
|
|
218
|
-
| Status | When | Body shape |
|
|
219
|
-
|---|---|---|
|
|
220
|
-
| [status] | [when it is returned] | [body shape] |
|
|
221
|
-
|
|
222
|
-
- **Maps to**: `[Component]` (§2.n)
|
|
223
|
-
|
|
224
|
-
#### Requests from the frontend lane
|
|
225
|
-
|
|
226
|
-
<!-- THE ANSWERS to the frontend plan's "Contract deltas needed" table, one row
|
|
227
|
-
per RQ-n it raises. PUBLISH: add the endpoint above — under the proposed
|
|
228
|
-
operation id unless it clashes with one this service already uses — and name
|
|
229
|
-
the id here. DECLINE: say why in one line; a request the spec does not
|
|
230
|
-
support is declined as a spec gap for `specify`, never published on a guess.
|
|
231
|
-
Keep every answered row; write `None` instead of the table when the frontend
|
|
232
|
-
plan raises no request — always so for a plan reversed from code, whose
|
|
233
|
-
frontend binds only to what the service already serves. -->
|
|
234
|
-
|
|
235
|
-
| Request | Outcome | Operation id | Why |
|
|
236
|
-
|---|---|---|---|
|
|
237
|
-
| RQ-[n] | published / declined | `[OperationId]` / — | [one line — required when declined] |
|
|
211
|
+
The endpoints this service publishes — route, verb, operation id, request, success and errors — and its answers to the frontend lane's requests are in [api-plan.md](api-plan.md), the API contract the frontend lane binds to.
|
|
238
212
|
|
|
239
213
|
### 2.[N] External Integrations
|
|
240
214
|
|
|
@@ -302,9 +276,9 @@
|
|
|
302
276
|
## Change History
|
|
303
277
|
|
|
304
278
|
<!-- A row is appended when this plan is RE-DERIVED to carry a change: the plan
|
|
305
|
-
is derived from spec.md
|
|
306
|
-
merges a change into those
|
|
307
|
-
it). close-delta bumps the version and stamps it into the header, so a header
|
|
279
|
+
is derived from spec.md, the context's domain-model.md and api-plan.md, and
|
|
280
|
+
finalize-delta merges a change into those three, never into this one (`plan
|
|
281
|
+
main` re-derives it). close-delta bumps the version and stamps it into the header, so a header
|
|
308
282
|
ahead of the newest row means the plan has not been re-derived since. A
|
|
309
283
|
change is planned as a DELTA in the change's own directory, never as a new
|
|
310
284
|
plan document. Newest last. -->
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
|
|
48
48
|
**Delta spec**: `changes/[CR-ID]/delta-spec.md`
|
|
49
49
|
|
|
50
|
-
**
|
|
50
|
+
**API delta**: [`changes/[CR-ID]/delta-api-plan.md`, or `None — the change leaves the API unchanged`]
|
|
51
51
|
|
|
52
52
|
<!--
|
|
53
53
|
This delta applies to the SINGLE master plan at
|
|
@@ -82,7 +82,7 @@ it serves.]
|
|
|
82
82
|
"Section" names a section of THIS project's master frontend plan — the same
|
|
83
83
|
section titles the generated frontend plan template carries. A binding row's
|
|
84
84
|
element LEADS with the operation id the service names the endpoint by — implement
|
|
85
|
-
checks it against the
|
|
85
|
+
checks it against the API document combined from the API plans' drafts. -->
|
|
86
86
|
|
|
87
87
|
| Section (§ in frontend-plan.md) | Element | Kind | Serves |
|
|
88
88
|
|---|---|---|---|
|
|
@@ -115,8 +115,9 @@ it serves.]
|
|
|
115
115
|
## Contract impact
|
|
116
116
|
|
|
117
117
|
[What this change needs from the service lane, bound against its published surface —
|
|
118
|
-
the delta-spec's IC- ids and the
|
|
119
|
-
|
|
118
|
+
the delta-spec's IC- ids and the change's API delta (delta-api-plan.md) — its Contract
|
|
119
|
+
deltas table and endpoint blocks — and, for an endpoint the change leaves alone, the
|
|
120
|
+
feature's api-plan.md: bindings added, changed or no longer used. Anything the experience
|
|
120
121
|
requires that the contract does not offer is raised back to the service lane —
|
|
121
122
|
never worked around in the client. Or `None`.]
|
|
122
123
|
|
|
@@ -150,8 +151,9 @@ shared state container or a navigation edge changed.]
|
|
|
150
151
|
## On merge
|
|
151
152
|
|
|
152
153
|
`finalize-delta` merges the change's SPEC half into `spec.md` and its MODEL half into the
|
|
153
|
-
context's `domain-model.md`, in one transaction,
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
context's `domain-model.md`, in one transaction, merges its `delta-api-plan.md` into the
|
|
155
|
+
feature's `api-plan.md` by script, and leaves the change OPEN. It does NOT merge this
|
|
156
|
+
document: the master plan is derived from those three, so `plan main` re-derives it rather
|
|
157
|
+
than patching it. `close-delta` archives this file with the change and bumps the
|
|
156
158
|
ONE version both lanes share. None of it is this document's job — it records the change,
|
|
157
159
|
it does not apply it.
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
|
|
41
41
|
§5 IS THE LANE BOUNDARY. Contract binding is the only place the service lane
|
|
42
42
|
enters: the frontend plan reads the WHOLE spec but only the service's
|
|
43
|
-
PUBLISHED SURFACE — the spec's IC- ids and the
|
|
43
|
+
PUBLISHED SURFACE — the spec's IC- ids and the endpoints api-plan.md
|
|
44
44
|
states. (⌛ `plan` will emit that surface as a design-contract.md projection;
|
|
45
45
|
the rule does not change when it does, only what §5 reads.) The requirement
|
|
46
46
|
is shared and must not fork; the implementation is not shared and must not
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
the aggregate's structure. This lane consumes the published surface at §5, not
|
|
93
93
|
the domain, so the model is not in its context.)
|
|
94
94
|
- THE PUBLISHED SURFACE IS THE ONLY SERVICE INPUT (§5) — the spec's IC- ids and
|
|
95
|
-
the
|
|
95
|
+
the endpoints api-plan.md states, never the service's internals.
|
|
96
96
|
|
|
97
97
|
STRUCTURE FOLLOWS THIS LANE'S CODE
|
|
98
98
|
- [GENERATED_LANE_STRUCTURE_NOTE] — form: one or two sentences naming the
|
|
@@ -219,11 +219,11 @@ does none.]
|
|
|
219
219
|
|
|
220
220
|
<!-- THE LANE BOUNDARY. One row per service operation this feature's UI depends
|
|
221
221
|
on, bound against the service's PUBLISHED SURFACE — the spec's IC- ids and
|
|
222
|
-
the
|
|
222
|
+
the endpoints api-plan.md states — never its internals: no table,
|
|
223
223
|
handler or persistence assumption crosses this line. Where the surface does
|
|
224
224
|
not name an operation yet, bind against the spec's own IC-/FR- surface and
|
|
225
225
|
record that as a §8 assumption. The OPERATION ID is the endpoint's name in the
|
|
226
|
-
service's published API document (as
|
|
226
|
+
service's published API document (as api-plan.md records it) — implement
|
|
227
227
|
finds the endpoint by it; `—` where none is named yet. -->
|
|
228
228
|
|
|
229
229
|
| Screen / component | Operation id | Operation (from the contract) | Sends (field shape) | Receives (field shape) | Failure handling | Serves |
|
|
@@ -234,7 +234,7 @@ does none.]
|
|
|
234
234
|
|
|
235
235
|
<!-- THE REQUEST LEDGER. One row per operation the experience needs that the
|
|
236
236
|
contract does not offer — raised back to the service lane, never worked
|
|
237
|
-
around in the client. Each request keeps its RQ-n for life. The
|
|
237
|
+
around in the client. Each request keeps its RQ-n for life. The API plan
|
|
238
238
|
answers in its "Requests from the frontend lane" table, published under an
|
|
239
239
|
operation id or declined with the reason; an answered request is re-bound
|
|
240
240
|
above to that id. Write `None` instead of the table when there is none. -->
|