@awak-app/simy-cli 0.5.11 → 0.5.12
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/package.json +1 -1
- package/src/cli-contract.js +1 -1
- package/src/codex-plugins/simy/skills/agentic-delivery-loop/SKILL.md +35 -4
- package/src/codex-plugins/simy/skills/agentic-delivery-loop/references/acceptance-evidence.md +25 -1
- package/src/codex-plugins/simy/skills/agentic-delivery-loop/references/minimum-completion.md +14 -5
- package/src/codex-plugins/simy/skills/agentic-delivery-loop/references/shared-system-specifications.md +21 -8
- package/src/codex-plugins/simy/skills/agentic-delivery-loop/references/state-and-receipts.md +5 -0
- package/src/codex-plugins/simy/skills/agentic-delivery-loop/scripts/check-acceptance.mjs +23 -5
- package/src/codex-plugins/simy/skills/agentic-delivery-loop/scripts/check-minimum-completion.mjs +54 -2
- package/src/orchestrator/completion-contract.js +50 -4
- package/src/orchestrator/contract.js +12 -1
- package/src/orchestrator/independent-audit.js +6 -5
- package/src/orchestrator/instruction.js +9 -3
- package/src/orchestrator/minimum-completion.js +1 -1
package/package.json
CHANGED
package/src/cli-contract.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from "./durable-local-task-steps-contract.js";
|
|
14
14
|
import { buildExecutionCapabilityContract } from "./execution-capability-contract.js";
|
|
15
15
|
|
|
16
|
-
export const CLI_VERSION = "0.5.
|
|
16
|
+
export const CLI_VERSION = "0.5.12";
|
|
17
17
|
export const CLI_API_CONTRACT_VERSION = 10;
|
|
18
18
|
|
|
19
19
|
export function withCliContract(
|
|
@@ -33,9 +33,20 @@ external-message authority and never weakens repository-required gates.
|
|
|
33
33
|
|
|
34
34
|
## Freeze the delivery charter
|
|
35
35
|
|
|
36
|
+
Start with the beneficiary's experience, not the proposed solution. Read-only
|
|
37
|
+
discovery may supply facts, but before solution design or coding freeze a compact
|
|
38
|
+
experience contract: who is in what situation, what they are trying to achieve,
|
|
39
|
+
the complete usable outcome, what they should understand and do next at each
|
|
40
|
+
observable moment, recovery and forbidden experiences, and how each moment will
|
|
41
|
+
be observed. Give each moment a stable UX/acceptance ID and carry those same IDs
|
|
42
|
+
through the shared To-Be, implementation mapping, and results matrix. Do not use
|
|
43
|
+
a list of components, endpoints, screens, or tasks as a substitute for the
|
|
44
|
+
end-to-end experience.
|
|
45
|
+
|
|
36
46
|
Before material action, record:
|
|
37
47
|
|
|
38
|
-
- objective and user-visible
|
|
48
|
+
- objective, beneficiary, and the frozen experience contract with user-visible
|
|
49
|
+
success conditions;
|
|
39
50
|
- authoritative acceptance sources that are independent of the candidate
|
|
40
51
|
implementation, plus architecture boundaries and dependency direction;
|
|
41
52
|
- repositories, latest base branches, dedicated worktrees, environments, and
|
|
@@ -58,8 +69,9 @@ changes the requested result.
|
|
|
58
69
|
|
|
59
70
|
Before implementation, read [shared system specifications](references/shared-system-specifications.md).
|
|
60
71
|
Find the team's canonical specification for this system; create it once if absent.
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
Reconcile the frozen experience contract with the current As-Is, then update only
|
|
73
|
+
the affected To-Be views. Preserve the purpose, beneficiary, current pain,
|
|
74
|
+
intended user experience, observable success, and stable UX/acceptance IDs.
|
|
63
75
|
Maintain the twelve-view inventory, stable IDs and cross-view impact links in the
|
|
64
76
|
shared source, not twelve new task-local copies. Unaffected views retain their
|
|
65
77
|
version; unknown As-Is facts remain unknown. Review changes through the team's
|
|
@@ -84,6 +96,10 @@ that historical failures cannot recur.
|
|
|
84
96
|
Before implementation, read [acceptance evidence](references/acceptance-evidence.md)
|
|
85
97
|
and persist a criterion-by-criterion checklist. Bind the criteria and both
|
|
86
98
|
verification layers to independent acceptance sources in a frozen charter.
|
|
99
|
+
Use the experience contract as the user-facing slice of these same criteria, not
|
|
100
|
+
as a parallel checklist. Every observable moment from the real trigger through a
|
|
101
|
+
usable outcome and applicable recovery path must map to at least one criterion;
|
|
102
|
+
working components do not prove that the combined experience is complete.
|
|
87
103
|
Use a separate current-results matrix for observations, status, evidence,
|
|
88
104
|
unmet reasons, and next actions. Render a readable table alongside the JSON;
|
|
89
105
|
checklists held only in chat or the model's memory are insufficient.
|
|
@@ -100,6 +116,15 @@ values, ownership, authorization, and duplication. HTTP 500/network failures are
|
|
|
100
116
|
failure evidence; absence of a 500 or presence of a 200 does not prove persistence.
|
|
101
117
|
Record justified non-applicability before execution, not after a failed test.
|
|
102
118
|
|
|
119
|
+
After building the exact candidate in the production-like local environment,
|
|
120
|
+
execute the complete experience and regenerate the readable table. It must let a
|
|
121
|
+
reviewer compare, for every stable ID, the persona/context and expected visible
|
|
122
|
+
outcome, the actual user observation, the independent technical postcondition or
|
|
123
|
+
integration result, evidence, status, and the next repair. Inspect the table as a
|
|
124
|
+
whole to catch gaps between individually working parts. Any missing, pending,
|
|
125
|
+
failed, stale, or unverifiable applicable experience row blocks
|
|
126
|
+
`user_like_local_verified` and returns the loop to the smallest affected repair.
|
|
127
|
+
|
|
103
128
|
Run the packaged acceptance validator before promoting a relevant verification
|
|
104
129
|
gate and before issuing the completion receipt. A missing, pending, failed, stale,
|
|
105
130
|
or unverifiable applicable row prevents promotion. Matrix completeness is one
|
|
@@ -114,7 +139,7 @@ For persistence, require reload/session return and stored values/ownership. A
|
|
|
114
139
|
working control, success message or HTTP 200 alone is insufficient.
|
|
115
140
|
|
|
116
141
|
The bundled minimum checker extends the acceptance matrix with structured
|
|
117
|
-
observations. Compare the original request to the
|
|
142
|
+
observations. Compare the original request to the stable criterion-ID list independently so
|
|
118
143
|
omitted requirements cannot disappear before validation. A missing, failed or
|
|
119
144
|
stale floor check prevents verified completion at any level. The CLI now enforces
|
|
120
145
|
this before implementation/PR promotion and on recheck; the standalone command
|
|
@@ -301,6 +326,12 @@ the charter permits, then have Codex operate the user's Chrome through the
|
|
|
301
326
|
browser extension as a normal user. Do not replace this gate with direct API
|
|
302
327
|
calls or a fixed selector-only test.
|
|
303
328
|
|
|
329
|
+
Immediately after the journey, refresh and inspect the experience/acceptance table
|
|
330
|
+
for the same candidate revision, environment, fixture, and input. Compare the
|
|
331
|
+
entire trigger-to-usable-outcome path, including what the user understands and can
|
|
332
|
+
do next, with the frozen experience contract. Do not promote the local journey
|
|
333
|
+
because its individual controls, requests, or services worked in isolation.
|
|
334
|
+
|
|
304
335
|
Read [Codex Chrome user verification](references/chrome-user-verification.md)
|
|
305
336
|
before running this phase.
|
|
306
337
|
|
package/src/codex-plugins/simy/skills/agentic-delivery-loop/references/acceptance-evidence.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Before implementation
|
|
4
4
|
|
|
5
|
+
Freeze the user experience before proposing the solution. Read-only discovery may
|
|
6
|
+
inform it, but the first authored success record states the beneficiary and
|
|
7
|
+
context, their goal, the complete usable outcome, what they should see,
|
|
8
|
+
understand, and be able to do next at each observable moment, applicable recovery
|
|
9
|
+
and forbidden experiences, and how those moments will be observed. Assign stable
|
|
10
|
+
UX/acceptance IDs. A screen, component, endpoint, task, or stored row is an
|
|
11
|
+
implementation detail unless it is tied to this experience.
|
|
12
|
+
|
|
5
13
|
The controller externalizes every acceptance condition with a stable ID, observable
|
|
6
14
|
outcome, source independent of the candidate, and required checks. For each
|
|
7
15
|
verification gate, freeze its applicable criteria in `charter.json` and record the
|
|
@@ -9,6 +17,13 @@ SHA-256 of the exact file bytes in the controller-owned snapshot. Reconcile the
|
|
|
9
17
|
gate charters against the overall delivery charter so no requirement disappears.
|
|
10
18
|
Do not choose a convenient subset after observing failures.
|
|
11
19
|
|
|
20
|
+
Use these criteria as the structured form of the experience contract rather than
|
|
21
|
+
creating a second UX checklist. Cover the full path from its real trigger to its
|
|
22
|
+
usable outcome and the applicable recovery path. When several parts must work
|
|
23
|
+
together for the beneficiary to succeed, include an end-to-end criterion in
|
|
24
|
+
addition to narrower diagnostic criteria. Independent coverage review compares
|
|
25
|
+
the experience contract and original request to the frozen rows before coding.
|
|
26
|
+
|
|
12
27
|
Each criterion has two layers:
|
|
13
28
|
|
|
14
29
|
- `user`: a goal-driven user journey with visible interactions and evidence of the
|
|
@@ -49,6 +64,15 @@ After every evaluated action (test, repair, review or changed inputs):
|
|
|
49
64
|
All rows, including pending/failed ones, stay visible. A green matrix cannot advance
|
|
50
65
|
an unrelated gate or prove merge/deployment. Validate again for the final environment.
|
|
51
66
|
|
|
67
|
+
After the exact candidate is built in a production-like local environment, run
|
|
68
|
+
the complete experience and regenerate `checklist.md` for that same revision,
|
|
69
|
+
environment, fixture, and input. Read the user and system rows together as a
|
|
70
|
+
single experience table: expected outcome, actual user observation, independent
|
|
71
|
+
postcondition/integration result, evidence, status, and next repair. Missing,
|
|
72
|
+
pending, failed, stale, or unverifiable applicable rows prevent
|
|
73
|
+
`user_like_local_verified`. Individually working components cannot override a
|
|
74
|
+
failed or absent end-to-end row.
|
|
75
|
+
|
|
52
76
|
## File contract
|
|
53
77
|
|
|
54
78
|
Charter example (illustrative values must be replaced with actual requirements):
|
|
@@ -59,7 +83,7 @@ Charter example (illustrative values must be replaced with actual requirements):
|
|
|
59
83
|
"run_id": "save-run-42",
|
|
60
84
|
"criteria": [{
|
|
61
85
|
"id": "SAVE-1",
|
|
62
|
-
"outcome": "
|
|
86
|
+
"outcome": "An owner returning to the task sees the saved note, understands it was retained, and can continue editing",
|
|
63
87
|
"acceptance_source": "Approved issue acceptance criteria, revision 3",
|
|
64
88
|
"layers": {
|
|
65
89
|
"user": {"applicability": "required", "method": "Save in Chrome, reload, sign in again and compare the note"},
|
package/src/codex-plugins/simy/skills/agentic-delivery-loop/references/minimum-completion.md
CHANGED
|
@@ -9,19 +9,25 @@ in an executor result or assurance-level selection disables the gate.
|
|
|
9
9
|
## Collect real observations
|
|
10
10
|
|
|
11
11
|
Before implementation, reconcile the original request, acceptance criteria and
|
|
12
|
-
planned operations
|
|
12
|
+
planned operations against the frozen experience contract. Preserve every
|
|
13
|
+
observable moment from the real trigger to a usable result, including applicable
|
|
14
|
+
recovery and forbidden experiences; a missing requirement must
|
|
13
15
|
be added through the controller's charter process, not omitted from the matrix.
|
|
14
16
|
The independent auditor compares original-request coverage and checks the oracle
|
|
15
17
|
against raw evidence. Machine mapping checks cannot discover an omitted idea in
|
|
16
18
|
natural language or authenticate what an evidence collector observed.
|
|
17
19
|
|
|
18
|
-
For each
|
|
20
|
+
For each stable criterion ID record implementation_refs (repository-relative file
|
|
21
|
+
paths). New UX-first charters preserve the IDs supplied by the experience
|
|
22
|
+
contract; legacy charters without explicit IDs retain deterministic `AC-N` IDs.
|
|
23
|
+
Then:
|
|
19
24
|
|
|
20
25
|
- user/operation: operate the actual UI or CLI/consumer. Expected is true; actual
|
|
21
26
|
is whether the intended operation really executed, not just whether a click
|
|
22
27
|
command succeeded. Include screenshots/traces where relevant.
|
|
23
28
|
- user/result: observe the requested visible outcome, including reload and session
|
|
24
|
-
return for a saving feature. Compare expected and actual values
|
|
29
|
+
return for a saving feature. Compare expected and actual values, user
|
|
30
|
+
understanding, and the next action the experience makes available.
|
|
25
31
|
- system/postcondition: independently inspect the final state; for persistence,
|
|
26
32
|
include the saved values and ownership, not a toast or HTTP status.
|
|
27
33
|
- system/integration: inspect the necessary combined path through APIs, storage,
|
|
@@ -33,6 +39,8 @@ For document-only work these mean opening the actual consumer and checking conte
|
|
|
33
39
|
links and packaged bytes. No DB/API is required if none participates. Both generic
|
|
34
40
|
layers remain required; there is no post-failure waiver. Tests, permissions or
|
|
35
41
|
runtime unavailable means pending/blocked. Never fabricate expected/actual equality.
|
|
42
|
+
Passing narrower rows for controls, APIs, services, or artifacts cannot substitute
|
|
43
|
+
for the end-to-end row when the requested experience depends on their combination.
|
|
36
44
|
|
|
37
45
|
## Executable contract
|
|
38
46
|
|
|
@@ -59,7 +67,8 @@ identities in the evidence; the independent reviewer must validate their current
|
|
|
59
67
|
identity. Do not claim that this single-checkout gate automatically re-reads a
|
|
60
68
|
companion service's Git HEAD or production environment.
|
|
61
69
|
|
|
62
|
-
Every matrix row
|
|
70
|
+
Every matrix row uses the same stable criterion ID and adds
|
|
71
|
+
`implementation_refs: ["src/example.js"]`. Each required
|
|
63
72
|
user/system cell's **first** evidence file is JSON with this shape (user example):
|
|
64
73
|
|
|
65
74
|
```json
|
|
@@ -128,7 +137,7 @@ external provider calls are introduced.
|
|
|
128
137
|
|
|
129
138
|
## Shared specifications and independent review
|
|
130
139
|
|
|
131
|
-
The floor's
|
|
140
|
+
The floor's stable criterion rows trace back to requirement IDs in the team's shared
|
|
132
141
|
As-Is/To-Be specification. Follow [shared system specifications](shared-system-specifications.md)
|
|
133
142
|
for purpose, user experience, cross-view consistency, independent reviews and
|
|
134
143
|
pre/post-PR SQM. These are controller workflow obligations, not fields magically
|
|
@@ -15,23 +15,36 @@ An old design or green PR is not proof of current production behavior.
|
|
|
15
15
|
|
|
16
16
|
At the start of each run:
|
|
17
17
|
|
|
18
|
-
1.
|
|
18
|
+
1. Freeze the experience contract from the original request before proposing a
|
|
19
|
+
solution: beneficiary/context, goal, complete usable outcome, observable
|
|
20
|
+
moments with what the person sees, understands, and can do next, recovery and
|
|
21
|
+
forbidden experiences, and the observation method. Read-only discovery may
|
|
22
|
+
inform this record. Give every moment a stable UX/acceptance ID.
|
|
23
|
+
2. Read the existing index and current linked views. Reconcile relevant As-Is with
|
|
19
24
|
code and actual behavior; label unknowns, cite evidence, and correct documented
|
|
20
25
|
drift without claiming that the drift was part of the requested feature.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
3. Reconcile why the change matters against that contract: current difficulty,
|
|
27
|
+
observable success, and non-goals. Preserve the user's original request as an
|
|
28
|
+
independent acceptance source; do not replace their outcome with a component
|
|
29
|
+
or implementation inventory.
|
|
30
|
+
4. Prepare a proposed To-Be on a branch/change proposal linked from the shared
|
|
25
31
|
system index. Record its baseline revision, affected stable IDs, before/after
|
|
26
32
|
behavior, dependencies and acceptance oracles. Reuse unchanged views by reference;
|
|
27
33
|
do not copy the whole specification into every run or generate empty diagrams.
|
|
28
|
-
|
|
34
|
+
5. Link each experience ID to workflow, screen/control, state/sequence, API/event,
|
|
29
35
|
data/relationship, permission and runtime boundaries as applicable. Map these
|
|
30
|
-
stable system IDs to the run's
|
|
31
|
-
|
|
36
|
+
stable system IDs to the run's criterion rows and implementation/evidence locations.
|
|
37
|
+
6. Obtain independent design review before implementation. Supply the original
|
|
32
38
|
request, shared baseline, To-Be and linked unchanged views. Missing evidence or
|
|
33
39
|
contradictory views remains pending/failed, never silently accepted.
|
|
34
40
|
|
|
41
|
+
After the exact local candidate is built, update the existing acceptance matrix
|
|
42
|
+
and its readable table for those same experience IDs. Compare expected experience,
|
|
43
|
+
actual user observation, independent system result, evidence, status, and next
|
|
44
|
+
repair across the whole journey. This is a view of the canonical acceptance data,
|
|
45
|
+
not another specification. A gap between working parts blocks local user-journey
|
|
46
|
+
promotion even if every part passed a narrower test.
|
|
47
|
+
|
|
35
48
|
The twelve-view index must account for every category: present (with a canonical
|
|
36
49
|
link), unchanged (with a revision), not applicable (with a reason), or unknown
|
|
37
50
|
(with an owner/next action). Only create/update a diagram where it describes a
|
package/src/codex-plugins/simy/skills/agentic-delivery-loop/references/state-and-receipts.md
CHANGED
|
@@ -9,6 +9,8 @@ Persist one controller-owned snapshot with at least:
|
|
|
9
9
|
|
|
10
10
|
- `run_id`, `state`, monotonic `revision`, and terminal disposition;
|
|
11
11
|
- immutable explicit conditions and their stable digest;
|
|
12
|
+
- frozen experience-contract identities, their mapping to acceptance rows, and
|
|
13
|
+
the current readable local-experience table path and digest;
|
|
12
14
|
- acceptance-source identities and digests, architecture boundaries, applicable
|
|
13
15
|
rule decisions, risk tier, requested/effective delivery levels, the safety
|
|
14
16
|
ceiling reason, and chat-confirmation receipt;
|
|
@@ -156,5 +158,8 @@ with current context and the required user/system layers. After each evaluated
|
|
|
156
158
|
action, refresh the JSON matrix and its readable table; append a receipt for the
|
|
157
159
|
update. Keep the frozen charter distinct from mutable results. A charter patch
|
|
158
160
|
supersedes the old digest only after the controller verifies its authority.
|
|
161
|
+
For `user_like_local_verified`, the current table must cover the frozen experience
|
|
162
|
+
contract from real trigger to usable outcome for the exact local candidate. A
|
|
163
|
+
collection of narrower passing rows does not waive a missing end-to-end row.
|
|
159
164
|
|
|
160
165
|
The CLI records minimum evidence in observed_evidence.minimum_completion, independently of executor status. The independent audit is bound to its matrix digest. Legacy v2 histories remain readable, but missing minimum evidence cannot be reused as passing evidence on resume/recheck. Missing or unsupported charters require a refresh; they do not disable verification.
|
|
@@ -25,7 +25,15 @@ function requireValue(condition, message) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function validateCharter(charter) {
|
|
28
|
-
requireValue(object(charter) && charter.schema_version
|
|
28
|
+
requireValue(object(charter) && [1, 2].includes(charter.schema_version) && text(charter.run_id), "Invalid charter header");
|
|
29
|
+
if (charter.schema_version === 2) {
|
|
30
|
+
requireValue(object(charter.experience), "Experience contract required");
|
|
31
|
+
for (const field of ["beneficiary", "situation", "goal", "usable_outcome", "recovery"])
|
|
32
|
+
requireValue(text(charter.experience[field]), `Experience contract needs ${field}`);
|
|
33
|
+
requireValue(Array.isArray(charter.experience.forbidden_experiences) &&
|
|
34
|
+
charter.experience.forbidden_experiences.length > 0 &&
|
|
35
|
+
charter.experience.forbidden_experiences.every(text), "Experience contract needs forbidden experiences");
|
|
36
|
+
}
|
|
29
37
|
requireValue(Array.isArray(charter.criteria) && charter.criteria.length > 0, "Charter requires criteria");
|
|
30
38
|
const ids = new Set();
|
|
31
39
|
for (const criterion of charter.criteria) {
|
|
@@ -88,15 +96,24 @@ export async function readEvidenceArtifact(root, artifact) {
|
|
|
88
96
|
const tableText = (value) => String(value ?? "").replaceAll("|", "\\|").replace(/[\r\n]+/g, " ");
|
|
89
97
|
|
|
90
98
|
export function renderChecklist(report) {
|
|
99
|
+
const experienceContext = report.experience
|
|
100
|
+
? `${report.experience.beneficiary} / ${report.experience.situation}`
|
|
101
|
+
: "Legacy charter: persona and situation not recorded";
|
|
91
102
|
const header = [
|
|
92
|
-
"|
|
|
93
|
-
"| --- | --- | --- | --- | --- | --- |",
|
|
103
|
+
"| Experience criterion / expected outcome | Persona / situation | Layer / method | Status | Actual observation | Evidence | Unmet reason / next repair |",
|
|
104
|
+
"| --- | --- | --- | --- | --- | --- | --- |",
|
|
94
105
|
];
|
|
95
106
|
const rows = report.checks.map((row) => "| " + [
|
|
96
|
-
`${row.id}: ${row.outcome}`,
|
|
107
|
+
`${row.id}: ${row.outcome}`, experienceContext,
|
|
108
|
+
`${row.layer}: ${row.method}`, row.status,
|
|
97
109
|
row.observation, row.evidence.join(", "), [row.reason, row.next_action].filter(Boolean).join(" / "),
|
|
98
110
|
].map(tableText).join(" | ") + " |");
|
|
111
|
+
const context = report.context;
|
|
112
|
+
const candidate = context
|
|
113
|
+
? `Candidate context: stage=${tableText(context.stage)}; environment=${tableText(context.environment)}; fixture=${tableText(context.fixture_id)}; input_sha256=${tableText(context.input_sha256)}; source_revisions=${tableText(JSON.stringify(context.source_revisions))}`
|
|
114
|
+
: "Candidate context: unavailable";
|
|
99
115
|
return [...header, ...rows, "", `Validator: ${report.status}`,
|
|
116
|
+
candidate,
|
|
100
117
|
...report.errors.map((error) => `- ${tableText(error)}`)].join("\n") + "\n";
|
|
101
118
|
}
|
|
102
119
|
|
|
@@ -111,7 +128,8 @@ export async function checkAcceptance({ charterBytes, matrix, expectedCharterSha
|
|
|
111
128
|
requireValue(contextDigest(matrix.context) === expectedContextSha256, "Current context digest mismatch");
|
|
112
129
|
requireValue(Array.isArray(matrix.results), "Matrix requires results");
|
|
113
130
|
const root = await realpath(evidenceRoot);
|
|
114
|
-
const report = { status: "evidence_incomplete",
|
|
131
|
+
const report = { status: "evidence_incomplete", experience: charter.experience ?? null,
|
|
132
|
+
context: matrix.context, checks: [], errors: [] };
|
|
115
133
|
const results = new Map();
|
|
116
134
|
const expectedIds = new Set(charter.criteria.map((criterion) => criterion.id));
|
|
117
135
|
for (const row of matrix.results) {
|
package/src/codex-plugins/simy/skills/agentic-delivery-loop/scripts/check-minimum-completion.mjs
CHANGED
|
@@ -35,13 +35,22 @@ export function minimumCharter(charter) {
|
|
|
35
35
|
charter.completion_contract.minimum_scope || charter.acceptance_criteria,
|
|
36
36
|
non_goals: charter.completion_contract.non_goals || [],
|
|
37
37
|
};
|
|
38
|
+
const ids = criterionIds(
|
|
39
|
+
charter.completion_contract.criterion_ids,
|
|
40
|
+
charter.acceptance_criteria.length,
|
|
41
|
+
);
|
|
42
|
+
const experience = minimumExperience(
|
|
43
|
+
charter.completion_contract.experience,
|
|
44
|
+
charter.completion_contract.user_outcome || charter.requirement,
|
|
45
|
+
);
|
|
38
46
|
return {
|
|
39
|
-
schema_version:
|
|
47
|
+
schema_version: 2,
|
|
40
48
|
minimum_completion_version: MINIMUM_COMPLETION_VERSION,
|
|
41
49
|
run_id: charter.id,
|
|
42
50
|
source,
|
|
51
|
+
experience,
|
|
43
52
|
criteria: charter.acceptance_criteria.map((outcome, index) => ({
|
|
44
|
-
id:
|
|
53
|
+
id: ids[index],
|
|
45
54
|
outcome,
|
|
46
55
|
acceptance_source: `request:${contextDigest(source)}`,
|
|
47
56
|
layers: {
|
|
@@ -60,6 +69,49 @@ export function minimumCharter(charter) {
|
|
|
60
69
|
};
|
|
61
70
|
}
|
|
62
71
|
|
|
72
|
+
function criterionIds(value, count) {
|
|
73
|
+
if (value === undefined || value === null)
|
|
74
|
+
return Array.from({ length: count }, (_, index) => `AC-${index + 1}`);
|
|
75
|
+
if (!Array.isArray(value)) throw new Error("Invalid acceptance criterion IDs");
|
|
76
|
+
const ids = value.map((id) => (text(id) ? id.trim() : ""));
|
|
77
|
+
if (
|
|
78
|
+
ids.length !== count ||
|
|
79
|
+
new Set(ids).size !== ids.length ||
|
|
80
|
+
ids.some(
|
|
81
|
+
(id) => !/^[A-Za-z][A-Za-z0-9]*(?:[-_.][A-Za-z0-9]+)*$/.test(id),
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
throw new Error("Invalid acceptance criterion IDs");
|
|
85
|
+
return ids;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function minimumExperience(value, userOutcome) {
|
|
89
|
+
const source = object(value) ? value : {};
|
|
90
|
+
return {
|
|
91
|
+
beneficiary: text(source.beneficiary)
|
|
92
|
+
? source.beneficiary.trim()
|
|
93
|
+
: "The requester",
|
|
94
|
+
situation: text(source.situation)
|
|
95
|
+
? source.situation.trim()
|
|
96
|
+
: "Using the requested changed behavior",
|
|
97
|
+
goal: text(source.goal) ? source.goal.trim() : userOutcome,
|
|
98
|
+
usable_outcome: text(source.usable_outcome)
|
|
99
|
+
? source.usable_outcome.trim()
|
|
100
|
+
: userOutcome,
|
|
101
|
+
recovery: text(source.recovery)
|
|
102
|
+
? source.recovery.trim()
|
|
103
|
+
: "Unmet acceptance rows remain visible with the next repair.",
|
|
104
|
+
forbidden_experiences:
|
|
105
|
+
Array.isArray(source.forbidden_experiences) &&
|
|
106
|
+
source.forbidden_experiences.length > 0 &&
|
|
107
|
+
source.forbidden_experiences.every(text)
|
|
108
|
+
? source.forbidden_experiences.map((item) => item.trim())
|
|
109
|
+
: [
|
|
110
|
+
"Reporting completion from components or commands without the end-to-end outcome",
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
63
115
|
export function minimumContext(charter, headSha) {
|
|
64
116
|
const frozen = minimumCharter(charter);
|
|
65
117
|
return {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cleanString, stringArray } from "./shared.js";
|
|
2
2
|
|
|
3
3
|
export const COMPLETION_CONTRACT_VERSION = 2;
|
|
4
|
+
const criterionIdPattern = /^[A-Za-z][A-Za-z0-9]*(?:[-_.][A-Za-z0-9]+)*$/;
|
|
4
5
|
|
|
5
6
|
export function buildCompletionContract({
|
|
6
7
|
inherited,
|
|
@@ -12,11 +13,15 @@ export function buildCompletionContract({
|
|
|
12
13
|
const source = record(inherited);
|
|
13
14
|
const minimumScope = stringArray(source.minimum_scope);
|
|
14
15
|
const proofRequired = stringArray(source.proof_required);
|
|
16
|
+
const criteria = stringArray(acceptanceCriteria);
|
|
17
|
+
const userOutcome = cleanString(source.user_outcome) || cleanString(requirement);
|
|
15
18
|
return {
|
|
16
19
|
version: COMPLETION_CONTRACT_VERSION,
|
|
17
|
-
user_outcome:
|
|
20
|
+
user_outcome: userOutcome,
|
|
18
21
|
minimum_scope:
|
|
19
|
-
minimumScope.length > 0 ? minimumScope :
|
|
22
|
+
minimumScope.length > 0 ? minimumScope : criteria,
|
|
23
|
+
criterion_ids: criterionIds(source.criterion_ids, criteria.length),
|
|
24
|
+
experience: normalizeExperience(source.experience, userOutcome),
|
|
20
25
|
non_goals: stringArray(nonGoals),
|
|
21
26
|
proof_required: uniqueStrings([
|
|
22
27
|
"criterion_evidence",
|
|
@@ -29,12 +34,53 @@ export function buildCompletionContract({
|
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
export function acceptanceCriterionRows(charter) {
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
const statements = stringArray(charter?.acceptance_criteria);
|
|
38
|
+
const ids = criterionIds(
|
|
39
|
+
record(charter?.completion_contract).criterion_ids,
|
|
40
|
+
statements.length,
|
|
41
|
+
);
|
|
42
|
+
return statements.map((statement, index) => ({
|
|
43
|
+
id: ids[index],
|
|
34
44
|
statement,
|
|
35
45
|
}));
|
|
36
46
|
}
|
|
37
47
|
|
|
48
|
+
function criterionIds(value, count) {
|
|
49
|
+
if (value === undefined || value === null) {
|
|
50
|
+
return Array.from({ length: count }, (_, index) => `AC-${index + 1}`);
|
|
51
|
+
}
|
|
52
|
+
if (!Array.isArray(value)) throw new Error("Invalid acceptance criterion IDs");
|
|
53
|
+
const ids = value.map(cleanString);
|
|
54
|
+
if (
|
|
55
|
+
ids.length !== count ||
|
|
56
|
+
new Set(ids).size !== ids.length ||
|
|
57
|
+
ids.some((id) => !criterionIdPattern.test(id))
|
|
58
|
+
) {
|
|
59
|
+
throw new Error("Invalid acceptance criterion IDs");
|
|
60
|
+
}
|
|
61
|
+
return ids;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function normalizeExperience(value, userOutcome) {
|
|
65
|
+
const source = record(value);
|
|
66
|
+
return {
|
|
67
|
+
beneficiary: cleanString(source.beneficiary) || "The requester",
|
|
68
|
+
situation:
|
|
69
|
+
cleanString(source.situation) || "Using the requested changed behavior",
|
|
70
|
+
goal: cleanString(source.goal) || userOutcome,
|
|
71
|
+
usable_outcome: cleanString(source.usable_outcome) || userOutcome,
|
|
72
|
+
recovery:
|
|
73
|
+
cleanString(source.recovery) ||
|
|
74
|
+
"Unmet acceptance rows remain visible with the next repair.",
|
|
75
|
+
forbidden_experiences:
|
|
76
|
+
stringArray(source.forbidden_experiences).length > 0
|
|
77
|
+
? stringArray(source.forbidden_experiences)
|
|
78
|
+
: [
|
|
79
|
+
"Reporting completion from components or commands without the end-to-end outcome",
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
38
84
|
export function normalizeUncertaintyVerification(value) {
|
|
39
85
|
const source = record(value);
|
|
40
86
|
return {
|
|
@@ -65,6 +65,7 @@ export function createCodingLoopSnapshot({ runId, request, metadata = {} }) {
|
|
|
65
65
|
const inheritedArtifacts = stringArray(charterContext.artifacts_required);
|
|
66
66
|
const inheritedAssumptions = stringArray(charterContext.assumptions);
|
|
67
67
|
const inheritedCompletionContract = record(charterContext.completion_contract);
|
|
68
|
+
const inheritedExperienceContract = record(charterContext.experience_contract);
|
|
68
69
|
const finalAcceptanceCriteria =
|
|
69
70
|
acceptanceCriteria.length > 0
|
|
70
71
|
? acceptanceCriteria
|
|
@@ -157,7 +158,17 @@ export function createCodingLoopSnapshot({ runId, request, metadata = {} }) {
|
|
|
157
158
|
visual_review_required: browserEvidenceRequired,
|
|
158
159
|
},
|
|
159
160
|
completion_contract: buildCompletionContract({
|
|
160
|
-
inherited:
|
|
161
|
+
inherited: {
|
|
162
|
+
...inheritedCompletionContract,
|
|
163
|
+
...(Object.keys(inheritedExperienceContract).length > 0
|
|
164
|
+
? {
|
|
165
|
+
experience: inheritedExperienceContract,
|
|
166
|
+
criterion_ids:
|
|
167
|
+
inheritedExperienceContract.criterion_ids ??
|
|
168
|
+
inheritedCompletionContract.criterion_ids,
|
|
169
|
+
}
|
|
170
|
+
: {}),
|
|
171
|
+
},
|
|
161
172
|
requirement: cleanString(charterContext.user_goal) || cleanString(request.requirement),
|
|
162
173
|
acceptanceCriteria: finalAcceptanceCriteria,
|
|
163
174
|
nonGoals: finalNonGoals,
|
|
@@ -27,7 +27,8 @@ export function buildIndependentAuditInstruction(charter, attempt) {
|
|
|
27
27
|
[
|
|
28
28
|
`Requirement: ${charter.requirement}`,
|
|
29
29
|
`Original request: ${charter.thread_state?.original_request || charter.requirement}`,
|
|
30
|
-
`Acceptance criteria:\n${bullets(charter.
|
|
30
|
+
`Acceptance criteria:\n${bullets(acceptanceCriterionRows(charter).map(row => `${row.id}: ${row.statement}`))}`,
|
|
31
|
+
`Frozen experience (controller-owned): ${JSON.stringify(charter.completion_contract?.experience ?? null)}`,
|
|
31
32
|
`User-visible outcome: ${charter.completion_contract?.user_outcome || charter.requirement}`,
|
|
32
33
|
`Minimum scope:\n${bullets(charter.completion_contract?.minimum_scope || charter.acceptance_criteria)}`,
|
|
33
34
|
`Risk level: ${charter.risk.level}`,
|
|
@@ -62,8 +63,8 @@ export function buildIndependentAuditInstruction(charter, attempt) {
|
|
|
62
63
|
"Inspect the final diff and related code directly from the repository.",
|
|
63
64
|
"Look for unmet acceptance criteria, regressions, security issues, data and concurrency risks, missing tests, and unrelated changes.",
|
|
64
65
|
"Treat executor claims as untrusted until supported by repository evidence.",
|
|
65
|
-
"Compare the original request to the complete
|
|
66
|
-
"For every
|
|
66
|
+
"Compare the original request and frozen experience to the complete stable criterion-ID list and implementation mapping. Report forgotten features, dead controls, unsaved results and broken integration. Open the raw minimum evidence, verify its oracle and actual runtime observations; matching expected/actual JSON and hashes alone do not prove truth. Check reload/session return and persisted values/ownership when saving is required.",
|
|
67
|
+
"For every stable criterion ID, return a criterion_results entry only after directly checking its cited proof and observed outcome.",
|
|
67
68
|
charter.evidence_policy?.browser_required
|
|
68
69
|
? "Open and inspect the actual local screenshots or videos. Return visual_review.inspected=true only after checking their visible content against the acceptance criteria."
|
|
69
70
|
: "Do not require visual evidence when the Charter does not require browser evidence.",
|
|
@@ -75,9 +76,9 @@ export function buildIndependentAuditInstruction(charter, attempt) {
|
|
|
75
76
|
[
|
|
76
77
|
"Finish with exactly one line beginning SIMY_AUDIT_JSON: followed by one JSON object.",
|
|
77
78
|
"Required keys: passed, summary, findings, criterion_results, visual_review, coverage_review.",
|
|
78
|
-
"coverage_review must contain requirements_complete (boolean), unmapped_requirements (array), and observation explaining the comparison of the original request,
|
|
79
|
+
"coverage_review must contain requirements_complete (boolean), unmapped_requirements (array), and observation explaining the comparison of the original request, frozen experience, stable criterion-ID list, implementation and raw user/system proof. Use false and list gaps when anything is missing or unverified.",
|
|
79
80
|
"Each finding candidate must contain passed, code, severity, target, explanation, and repairability.",
|
|
80
|
-
"criterion_results must contain one entry per
|
|
81
|
+
"criterion_results must contain one entry per stable criterion ID with criterion_id, passed, verification_method, evidence_refs, and observed_result.",
|
|
81
82
|
"visual_review must contain inspected, artifacts, and acceptance_observations.",
|
|
82
83
|
"Only passed=false entries are findings. Omit passing checks, and never report passed=true evidence as a finding.",
|
|
83
84
|
"Use passed=false when a blocker or major finding remains, or when evidence is unavailable.",
|
|
@@ -37,6 +37,12 @@ export function buildCodingInstruction(
|
|
|
37
37
|
"completion_contract",
|
|
38
38
|
[
|
|
39
39
|
`User-visible outcome: ${charter.completion_contract?.user_outcome || charter.thread_state.user_goal}`,
|
|
40
|
+
`Beneficiary: ${charter.completion_contract?.experience?.beneficiary || "not recorded"}`,
|
|
41
|
+
`Situation: ${charter.completion_contract?.experience?.situation || "not recorded"}`,
|
|
42
|
+
`Experience goal: ${charter.completion_contract?.experience?.goal || "not recorded"}`,
|
|
43
|
+
`Usable outcome: ${charter.completion_contract?.experience?.usable_outcome || "not recorded"}`,
|
|
44
|
+
`Recovery experience: ${charter.completion_contract?.experience?.recovery || "not recorded"}`,
|
|
45
|
+
`Forbidden experiences:\n${bullets(charter.completion_contract?.experience?.forbidden_experiences || [])}`,
|
|
40
46
|
`Minimum scope:\n${bullets(charter.completion_contract?.minimum_scope || charter.acceptance_criteria)}`,
|
|
41
47
|
`Non-goals:\n${bullets(charter.completion_contract?.non_goals || charter.thread_state.non_goals)}`,
|
|
42
48
|
`Proof required:\n${bullets(charter.completion_contract?.proof_required || [])}`,
|
|
@@ -52,10 +58,10 @@ export function buildCodingInstruction(
|
|
|
52
58
|
),
|
|
53
59
|
),
|
|
54
60
|
section("minimum_completion_evidence", [
|
|
55
|
-
"All delivery levels require this floor. Before implementation reconcile every original requirement with the
|
|
61
|
+
"All delivery levels require this floor. Before implementation reconcile every original requirement and frozen experience with the stable criterion-ID list; report omissions instead of silently dropping them.",
|
|
56
62
|
"Save a matrix.json under the local evidence root (ui_evidence_root or repository .artifacts). Return delivery_evidence_path in SIMY_RESULT_JSON. A passed flag or tests_run string is not proof.",
|
|
57
63
|
"Use check-minimum-completion.mjs from the installed Delivery Loop skill. Its reference minimum-completion.md defines the matrix and structured observation format.",
|
|
58
|
-
"For each
|
|
64
|
+
"For each stable criterion ID include real implementation_refs and required user/system evidence. Exercise the actual operation and result, then inspect postcondition and integration independently. For saving, reload/return in a new session and verify stored values and ownership. Document-only work uses the real command/consumer and artifact integrity, not imaginary API/DB checks.",
|
|
59
65
|
"First evidence artifact per layer must be JSON: {criterion_id, context_sha256, checks}. User checks: operation (expected=true, actual=observed boolean) and result; system checks: postcondition and integration. Each check includes subject, expected and actual. Derive expected values from the original requirement, not from the candidate output. Include screenshots/logs as additional evidence.",
|
|
60
66
|
"Freeze and bind observations to the current committed revision and charter before verification. Do not rehash old observations to make changed code pass. Use pending/failed for unavailable or mismatching evidence.",
|
|
61
67
|
`Controller input JSON: ${JSON.stringify({ id: charter.id, requirement: charter.requirement, repository: charter.repository, acceptance_criteria: charter.acceptance_criteria, completion_contract: charter.completion_contract, thread_state: { original_request: charter.thread_state.original_request } })}`,
|
|
@@ -145,7 +151,7 @@ export function buildCodingInstruction(
|
|
|
145
151
|
"Required keys: outcome_kind, summary, branch_name, commit_sha, commit_message_headline, pr_url, pr_number, pr_title, pr_base_branch, tests_run, tests_passed, ui_evidence_path, unrelated_changes_detected, secret_scan_passed, changed_files, residual_risks, problem_solving, uncertainty_verification, criterion_evidence, visual_review, delivery_evidence_path.",
|
|
146
152
|
problemSolvingResultContract(),
|
|
147
153
|
"uncertainty_verification must contain top_uncertainty, first_verification, evidence_checked, observed_result, resolved, and fallback. Only set resolved=true when concrete observed evidence resolves it.",
|
|
148
|
-
"criterion_evidence must contain one entry for every
|
|
154
|
+
"criterion_evidence must contain one entry for every stable criterion ID with criterion_id, passed, verification_method, evidence_refs, and observed_result. Only set passed=true when the evidence directly proves that criterion.",
|
|
149
155
|
"For UI or browser-extension work, visual_review must contain inspected=true, the local screenshot or video paths in artifacts, and concrete acceptance_observations from viewing the actual content. Capturing a file without viewing it does not count.",
|
|
150
156
|
"Use null for unavailable scalar values and [] for unavailable arrays. Do not claim evidence that was not observed.",
|
|
151
157
|
].join("\n"),
|
|
@@ -69,7 +69,7 @@ export async function collectMinimumCompletion({
|
|
|
69
69
|
return {
|
|
70
70
|
status: "evidence_incomplete",
|
|
71
71
|
errors: [
|
|
72
|
-
"Minimum completion evidence missing, invalid or stale; verify the actual operation, result, postcondition and integration for every
|
|
72
|
+
"Minimum completion evidence missing, invalid or stale; verify the actual operation, result, postcondition and integration for every stable criterion ID.",
|
|
73
73
|
],
|
|
74
74
|
};
|
|
75
75
|
}
|