@abelxiaoxing/cadence 1.3.0 → 1.4.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/README.md +115 -10
- package/package.json +5 -3
- package/prompts/abel-design.md +71 -17
- package/prompts/abel-diagnose.md +10 -1
- package/prompts/abel-implement.md +118 -20
- package/prompts/abel-init.md +9 -2
- package/src/apply-transaction.ts +16 -14
- package/src/artifact-store.ts +17 -0
- package/src/candidate-patch.ts +2 -1
- package/src/canonical.ts +16 -0
- package/src/change-contract.ts +268 -0
- package/src/child-session.ts +4 -3
- package/src/contracts.ts +141 -33
- package/src/control-contracts.ts +51 -1
- package/src/delivery-compiler.ts +148 -31
- package/src/design-control.ts +69 -11
- package/src/design-journal.ts +216 -72
- package/src/durable-workflow.ts +4385 -0
- package/src/implement-graph.ts +11 -16
- package/src/index.ts +380 -981
- package/src/isolation-backend.ts +84 -23
- package/src/package-delivery.ts +486 -0
- package/src/package-verification.ts +509 -0
- package/src/route-policy.ts +1 -11
- package/src/run-state.ts +4 -16
- package/src/run-store.ts +5 -144
- package/src/scoped-grep-worker.mjs +23 -0
- package/src/scoped-tools.ts +312 -116
- package/src/sqlite-schema.ts +171 -0
- package/src/storage-schema.ts +352 -0
- package/src/subagent-activity.ts +14 -1
- package/src/submit-tool.ts +20 -2
- package/src/task-ledger.ts +14 -55
- package/src/verification-capability.ts +298 -102
- package/src/verification-environment.ts +213 -0
- package/src/verification-identity.ts +77 -0
- package/src/worker-broker.ts +97 -34
- package/src/workflow-engine.ts +28 -8285
- package/src/workflow-policy.ts +1427 -0
- package/src/workflow-state-machine.ts +4478 -0
- package/src/workspace-io-entry.mjs +58 -0
- package/src/workspace-io-worker.mjs +984 -0
- package/src/workspace-io.ts +84 -0
- package/src/workspace-store.ts +125 -19
- package/skills/abel-workflow/SKILL.md +0 -205
|
@@ -6,13 +6,21 @@ tags: [abel, implement, openspec, TDD, recovery]
|
|
|
6
6
|
argument-hint: "<change_name>"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
This procedure applies only when the user explicitly invokes `/abel-implement`.
|
|
10
|
+
Reading this file, mentioning the command, or finding OpenSpec artifacts does not activate it.
|
|
11
|
+
Read the complete value inside `<abel-request>` without tokenizing it a second time.
|
|
10
12
|
|
|
11
13
|
<abel-request>
|
|
12
14
|
$ARGUMENTS
|
|
13
15
|
</abel-request>
|
|
14
16
|
|
|
15
17
|
<!-- ABEL:PROMPT:abel-implement -->
|
|
18
|
+
|
|
19
|
+
This stage is scoped to the invoked task.
|
|
20
|
+
Direct answers and same-task continuations stay in this stage.
|
|
21
|
+
If the user ends the workflow or requests an unrelated task, first send `{"action":"finish"}` to `abel_dispatch`, then handle the new task normally with the restored tools.
|
|
22
|
+
Exit preserves resumable work and never means completion or discard.
|
|
23
|
+
Never start another stage automatically.
|
|
16
24
|
<!-- ABEL:START -->
|
|
17
25
|
|
|
18
26
|
# Implement outcome
|
|
@@ -22,14 +30,22 @@ If it is missing, absent, or ambiguous, stop before execution and ask for the un
|
|
|
22
30
|
|
|
23
31
|
Implement is a durable, resumable execution stage.
|
|
24
32
|
The private control plane owns task scheduling, Red-Green-Refactor progression, candidate application, verification attribution, repair, task checkbox updates, AGENTS checkpoints, and the final transaction.
|
|
25
|
-
The parent interprets state and
|
|
33
|
+
The parent interprets state, owns implementation decisions, and follows automatic continuations; it must not reproduce those mechanics manually.
|
|
34
|
+
Design has already established the goal, main choices, and explicit constraints.
|
|
35
|
+
Invoking Implement delegates remaining implementation choices to the parent model's recommended solution.
|
|
36
|
+
Choose the best supported option, record the concise decision and rationale in the same change, and continue without asking the user to select an option or confirm it again.
|
|
37
|
+
Respect explicit Design constraints and non-goals; when a proposed option conflicts, choose a compliant alternative.
|
|
38
|
+
A Worker request is evidence for the parent, not permission for the Worker to widen its own authority.
|
|
39
|
+
Do not end the turn merely because a control call returns a nonterminal state.
|
|
40
|
+
Follow its parent-owned continuation until verified completion or a concrete blocker that cannot be resolved within available capabilities and budgets.
|
|
41
|
+
Keep routine decisions and repairs internal; give brief progress updates during sustained work and include material implementation choices in the final result.
|
|
26
42
|
|
|
27
43
|
## Trusted admission
|
|
28
44
|
|
|
29
|
-
Before the first Worker launch, load the current `ready.yaml` and referenced canonical `implement-plan.json`; verify the Gate A binding, raw and canonical hashes, OpenSpec strict/planning status, Requirement → Scenario → Verification → Task traceability, capability closure, exact path/output contracts, repair policy, tracking contract, and sealed AGENTS operations.
|
|
45
|
+
Before the first Worker launch, load the current `ready.yaml` and referenced canonical `implement-plan.json`; verify both current owner-private Gate proofs and the finalization fact binding the exact delivery revision, receipt hash, and canonical plan hash, plus the Gate A binding, raw and canonical hashes, OpenSpec strict/planning status, Requirement → Scenario → Verification → Task traceability, capability closure, exact path/output contracts, repair policy, tracking contract, and sealed AGENTS operations.
|
|
30
46
|
|
|
31
47
|
An invalid or incomplete delivery is one aggregated `delivery-invalid` admission result.
|
|
32
|
-
|
|
48
|
+
Inspect every safe diagnostic together and use the returned parent-owned amendment continuation to repair an executable delivery before resuming.
|
|
33
49
|
Do not launch a Worker, consume a retry, mutate the main workspace, or infer a missing contract.
|
|
34
50
|
|
|
35
51
|
Gate approval is not tool permission.
|
|
@@ -37,7 +53,7 @@ A fresh context validates existing approval facts and does not ask the user to a
|
|
|
37
53
|
|
|
38
54
|
## Closed control surface
|
|
39
55
|
|
|
40
|
-
|
|
56
|
+
For durable run operations, use `abel_dispatch` only with the change commands below; never send `version`; never send `action: "run"`; and never send `admit-graph`, `task-attempt`, caller-owned snapshots, completed/blocked arrays, or apply identities.
|
|
41
57
|
|
|
42
58
|
```json
|
|
43
59
|
{"command":"start","stage":"abel-implement","change":"<change>","operationId":"<unique-operation>"}
|
|
@@ -48,16 +64,17 @@ Use `abel_dispatch` only with the change commands below; never send `version`; n
|
|
|
48
64
|
{"command":"discard","stage":"abel-implement","change":"<change>","operationId":"<unique-operation>"}
|
|
49
65
|
```
|
|
50
66
|
|
|
51
|
-
When
|
|
67
|
+
When a revised delivery is ready, resume the same run; the engine discovers and validates its `deliveryRevision` and `receiptHash` locally.
|
|
52
68
|
The engine revalidates retained phase/repair facts, invalidates only incompatible tasks, rebuilds tracking, and preserves compatible work.
|
|
53
69
|
|
|
54
70
|
`status` is local and must remain useful when every Worker endpoint is unavailable.
|
|
71
|
+
`discard` is an explicit destructive request; leaving the workflow uses `{"action":"finish"}` and preserves the run instead.
|
|
55
72
|
Repeating the same operation id returns its committed outcome.
|
|
56
73
|
Use a new operation id for a new attempt.
|
|
57
74
|
|
|
58
75
|
For an approval-needed retained run, a later explicit Implement invocation must inspect local `status`.
|
|
59
76
|
When a newer owner-private-proof-bound receipt is present, status exposes its exact pair in `availableDelivery`, adds `resume` to `legalCommands`, and fills the matching `conditionalCommands` arguments.
|
|
60
|
-
|
|
77
|
+
Ordinary resume resolves that pair locally; do not require the user to copy revision or hash data from a prior conversation.
|
|
61
78
|
Discovery never admits the delivery: `resume` still performs the full artifact, traceability, capability, and currentness validation before any Worker starts.
|
|
62
79
|
|
|
63
80
|
## Execution and verification
|
|
@@ -65,7 +82,7 @@ Discovery never admits the delivery: `resume` still performs the full artifact,
|
|
|
65
82
|
Before any candidate, the engine records target-contract, task-affected, and full-suite baselines with normalized failure identities.
|
|
66
83
|
A pre-existing failure remains separate and never satisfies Red or becomes evidence that this change caused it.
|
|
67
84
|
|
|
68
|
-
For each ready task the engine runs:
|
|
85
|
+
For each ready behavior task the engine runs:
|
|
69
86
|
|
|
70
87
|
1. Red in a disposable child revision; the declared target must fail with the approved identity.
|
|
71
88
|
2. Green with the minimum approved implementation; the declared target and task-affected contract must pass relative to baseline.
|
|
@@ -86,32 +103,93 @@ The main workspace stays unchanged before that transaction.
|
|
|
86
103
|
Ordinary failures stay inside this Implement run:
|
|
87
104
|
|
|
88
105
|
- `artifact`, wrong-Red identity, invalid/incomplete patch operation, stale candidate, or transport failure: use the bounded automatic attempt policy; after exhaustion pause with the last safe code and retain the last committed revision.
|
|
89
|
-
- Artifact correction
|
|
90
|
-
|
|
91
|
-
|
|
106
|
+
- Artifact, stale-candidate, and verification correction share the sealed `artifactCorrection.maxAttempts` of 2-3 attempts per verification obligation and phase, including the initial attempt.
|
|
107
|
+
Automatic retries carry the safe failure code, strategy, and available failure identities to the next Worker.
|
|
108
|
+
Exhaustion survives process restart and `resume`.
|
|
109
|
+
Operation ids, route binding, rollback revisions, task renaming, and contract rewording never clear it.
|
|
110
|
+
Dedicated private incident records own the budget; displayed attempt diagnostics are not authority.
|
|
111
|
+
A run shares one cumulative work budget: 24 plus three units per largest admitted phase count, bounded by the host limit captured at run start (default 512).
|
|
112
|
+
Recompiling, renaming or returning to a previous plan size never adds credit; a genuinely larger admitted decomposition may increase capacity without refunding consumption.
|
|
113
|
+
Execution reserves work durably before launch, including nested repair proposals; restart and post-launch cancellation do not refund consumed work.
|
|
114
|
+
- `environment` or `verification-adapter`: the executor pauses without speculative edits.
|
|
115
|
+
The parent inspects local diagnostics and restores available prerequisites using existing authorized capabilities, then resumes; baseline facts and completed phases are reused.
|
|
116
|
+
Do not request user input for a prerequisite the parent can restore.
|
|
117
|
+
Missing external credentials or unavailable services are reported only after local alternatives have been checked.
|
|
92
118
|
- `pre-existing`: keep as baseline evidence.
|
|
93
119
|
It does not authorize a code change and does not fail completion unless the change worsens it.
|
|
94
120
|
- `introduced` in a task-affected contract: reopen the owning task as `repairable`, perform the minimum bounded repair inside its approved paths, record `repair-verified`, and re-run attribution automatically.
|
|
95
121
|
- `unresolved` full-suite attribution: pause with failure identities and retained cumulative state; resume after evidence resolves ownership.
|
|
96
122
|
- `workspace-revision-stale`, conflict, cancellation, process interruption, or apply recovery: preserve user edits and resume from the last durable checkpoint.
|
|
97
|
-
Cancellation is budget-neutral.
|
|
123
|
+
Cancellation before execution is budget-neutral; cancellation after launch does not count as a product failure and does not refund consumed work.
|
|
98
124
|
During a partially visible final apply, recovery must settle before pause or discard.
|
|
99
|
-
- `result-limit
|
|
125
|
+
- `result-limit` or a task-splitting request inside existing authority: automatically try a compact complete patch using exact replacements under the same bounded policy.
|
|
126
|
+
If it still cannot fit, the executor pauses as `needs-task-split` or `task-split-needed` and supplies an amendment continuation.
|
|
127
|
+
The parent recommends a smaller task decomposition, compiles the revised DAG, and resumes; never accept a partial patch or bypass compilation of the task DAG.
|
|
100
128
|
|
|
101
129
|
No item above carries stage-routing metadata or asks the user to restart the approved workflow.
|
|
102
130
|
|
|
131
|
+
Treat ordinary recovery as internal work.
|
|
132
|
+
Workers may request additional ordinary regular-file reads within sealed task roots.
|
|
133
|
+
The parent grants exact paths, retains their content dependencies across phases and restart, and binds them to merge and final currentness; hidden files, private-key files, out-of-root paths, and writes do not receive this discovery permission.
|
|
134
|
+
Do not ask permission to correct approved code/tests, use task-wide approved read context, or retry within the sealed policy.
|
|
135
|
+
Use local status and read-only diagnostics to identify environmental prerequisites before asking the user; resume once a concrete capability has been restored, and reuse baseline facts and completed phases.
|
|
136
|
+
When status reports `recovery.exhausted`, do not issue unchanged `resume` calls.
|
|
137
|
+
Inspect retained evidence.
|
|
138
|
+
If status supplies a parent-owned amendment continuation, use it for a concrete plan correction; otherwise explain the specific missing capability or exhausted budget once.
|
|
139
|
+
Rewriting prose or renaming tasks is not a correction and never replenishes execution budgets.
|
|
140
|
+
A rebind may repair endpoint configuration but does not grant a new incident budget; do not repeatedly rebind and resume the same failure.
|
|
141
|
+
If a revised delivery already exists, local status also discovers its exact revision/hash for an exhausted run; resume with `availableDelivery` without asking the user to copy receipt data.
|
|
142
|
+
When `recovery.additionalAttempt` is present, the parent may make a concrete decision to retry once within remaining work capacity.
|
|
143
|
+
Copy that object into an ordinary resume request's optional `recovery` field; do not invent incident keys or failure sequences.
|
|
144
|
+
The default reason is `parent-directed-retry`; `route-changed` and `context-extended` additionally require control-plane evidence.
|
|
145
|
+
This consumes one launch without resetting automatic correction history; another failure pauses again.
|
|
146
|
+
Do not turn the availability of this option into an unconditional retry loop or another user approval round.
|
|
147
|
+
Environment, resource and report protocol failures are unavailable verification, not product failures; restore prerequisites and reverify retained candidates.
|
|
148
|
+
An unavailable endpoint, missing external credential, or irreducibly oversized task is not proof that product Design must restart.
|
|
149
|
+
|
|
103
150
|
## The only approval-needed boundary
|
|
104
151
|
|
|
105
|
-
Use `approval-needed` only when continuing requires authority not present in the sealed delivery: new observable behavior, architecture or policy; a new/changed dependency; an undeclared write/delete
|
|
152
|
+
Use `approval-needed` only when continuing requires authority not present in the sealed delivery: new observable behavior, architecture or policy; a new/changed dependency; an undeclared write/delete target or a read outside sealed discovery roots; expanded conflict/resource authority; a changed verification contract; an AGENTS target/impact/managed block change; or another irreversible scope decision.
|
|
106
153
|
|
|
107
154
|
Classify only codes in the closed authority table.
|
|
108
155
|
An unknown Worker, verifier, or internal approval code is an `approval-code-invalid` integrity pause; never guess a category or Gate from keywords.
|
|
109
156
|
|
|
110
|
-
Report
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
157
|
+
Report all known missing authority together using `blockers` and `decisionBatch.items`; merge related choices and reuse accepted same-task decisions.
|
|
158
|
+
The parent resolves new implementation choices with its recommended option under the accepted Design; do not ask the user for another decision round.
|
|
159
|
+
`approval-needed` is an internal requirement to compile updated authority before a Worker continues, not a request for user input.
|
|
160
|
+
`decisionBatch.resolution` identifies the parent as decision owner and `continuation` identifies the automatic next action.
|
|
161
|
+
Follow it in this turn.
|
|
162
|
+
`owner` and `automatic` are status metadata, not tool arguments.
|
|
163
|
+
For amendment use the exact `decisionBatch.continuation` envelope plus `request`; for resume send the closed command envelope with a new operationId.
|
|
164
|
+
A changed batch must cite the new evidence; never ask separately for each task's copy of the same decision.
|
|
165
|
+
Retain the Implement stage and original run.
|
|
166
|
+
`decisionBatch.continuation` supplies the exact `action: "amend"`, change, and batchId for the narrow private artifact revision channel.
|
|
167
|
+
After selecting the recommended solution, use that envelope with a `request` containing the existing closed Design artifact operations: start the same change, retain inherited decisions, record the parent-selected changes, write the required change artifacts, compile, and finalize.
|
|
168
|
+
This is implementation delegation from the accepted Design, not a fabricated new user answer.
|
|
169
|
+
The same channel is available for the allowlisted `delivery-invalid`, `needs-task-split`, and `task-split-needed` technical pauses; these do not need a user decision or a fabricated approval-needed classification.
|
|
170
|
+
Each run permits 64 mutating amendment attempts in a dedicated persistent budget.
|
|
171
|
+
Failed writes/compilations consume that budget, successful operation replay is free, and restart or a new batch does not replenish it.
|
|
172
|
+
Read-only status/preflight does not consume it.
|
|
173
|
+
Fix the exact structured diagnostic before retrying compilation.
|
|
174
|
+
Do not repeatedly submit unchanged invalid artifacts; once `amendmentBudget.exhausted` is true, stop mutation and report the retained result truthfully.
|
|
175
|
+
For example, begin the revision with the returned change and batchId:
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{"action":"amend","change":"<same-change>","batchId":"<current-batch-id>","request":{"operation":"start","change":"<same-change>","operationId":"<unique-operation>"}}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Use the returned private revision runId on subsequent amendment requests.
|
|
182
|
+
This channel does not activate Design, admit evidence packets, allow a different change, or grant product/AGENTS file writes.
|
|
183
|
+
Keep the approved goal and explicit constraints stable.
|
|
184
|
+
Automatic amendments retain Gate A and the structured ChangeContract.
|
|
185
|
+
They cannot renew Gate A, replace behavior decisions, or rewrite proposal/spec artifacts.
|
|
186
|
+
Resolve implementation gaps within the accepted goal, constraints and policy; when no conforming solution exists, report that concrete contract blocker and preserve progress rather than repeatedly attempting an invalid amendment.
|
|
187
|
+
Never weaken acceptance criteria just to obtain a passing result.
|
|
188
|
+
The compiler supplies Gate B automatically.
|
|
189
|
+
Finish all known items in the batch before finalizing, then issue ordinary resume.
|
|
190
|
+
Local delivery discovery supplies the newer verified revision/hash; do not ask the user to copy receipts or switch commands.
|
|
191
|
+
An unchanged resume cannot bypass a pending decision, and stale batch ids or unrelated revision runIds are rejected.
|
|
192
|
+
The user may explicitly choose a full Design discussion, but it is not the recovery instruction for an Implement blocker.
|
|
115
193
|
A generated artifact defect, endpoint outage, stale snapshot, environment failure, pre-existing failure, introduced in-boundary repair, approved documentation/test edit, or approved AGENTS checkpoint is never `approval-needed`.
|
|
116
194
|
|
|
117
195
|
## AGENTS and tracking
|
|
@@ -120,9 +198,14 @@ Design-time AGENTS read-only authority does not carry into Implement.
|
|
|
120
198
|
Gate B seals `none | update-existing | create-index | remove-index`, exact targets, owning task ids, verification, and the complete managed block.
|
|
121
199
|
The parent control plane applies those operations only in the private cumulative revision, preserves all human text outside `<!-- ABEL:AGENTS-INDEX:START -->` and `<!-- ABEL:AGENTS-INDEX:END -->`, and never gives an AGENTS write path to a Worker.
|
|
122
200
|
|
|
201
|
+
`remove-index` deletes a file only if no human content remains.
|
|
202
|
+
Runtime/session ids, credentials, approval state, and dirty-state ledgers never enter an index.
|
|
203
|
+
|
|
123
204
|
Task checkboxes are parent-owned progress facts.
|
|
124
205
|
The loader may normalize only `[x]`/`[X]` to `[ ]` for the sealed task ids when checking the delivery hash; every other byte remains integrity-bound.
|
|
125
206
|
|
|
207
|
+
When an approved browser E2E check requires `dev-browser` and it is unavailable, pause only that check; continue independent work when safe.
|
|
208
|
+
|
|
126
209
|
## Truthful completion
|
|
127
210
|
|
|
128
211
|
Treat `queued`, `connecting`, `waiting-first-response`, `running`, `validating`, `retrying`, `verifying`, `paused`, `approval-needed`, `applying`, and `recovering` as nonterminal activity states.
|
|
@@ -131,9 +214,24 @@ Tool-call settlement is not success.
|
|
|
131
214
|
Report success only when the durable state is `completed`, final application and post-apply verification committed, and private run content was cleaned.
|
|
132
215
|
Terminal `completed`, `discarded`, or `rejected` settlement deactivates private dispatch; a nonterminal pause or approval wait keeps it active for direct follow-up.
|
|
133
216
|
|
|
134
|
-
On a nonterminal result
|
|
217
|
+
On a nonterminal result with an automatic parent continuation, execute it and continue working; do not present internal Gate names, batch ids, receipts, or resume commands as user tasks.
|
|
218
|
+
If automatic recovery is unavailable after investigation or its budget is exhausted, report the concrete blocker, retained progress, and only the external input needed to proceed.
|
|
219
|
+
Respect explicit cancellation and never resume it automatically.
|
|
135
220
|
Never hide an incomplete run behind a success checkmark.
|
|
136
221
|
|
|
137
222
|
Do not archive, publish, release, stage, or commit implicitly.
|
|
138
223
|
|
|
139
224
|
<!-- ABEL:END -->
|
|
225
|
+
|
|
226
|
+
## Evidence modes and environment identity
|
|
227
|
+
|
|
228
|
+
Follow the compiled task `verificationMode`.
|
|
229
|
+
The default `behavior` mode retains Red/Green.
|
|
230
|
+
Explicitly approved `mechanical` and `refactor` modes start execution at Green after baseline capture; do not invent a failing test or request a Red Worker.
|
|
231
|
+
The compiler checks mode authority, path suitability and protected verifier inputs.
|
|
232
|
+
All modes retain cumulative verification, output checks and transactional apply.
|
|
233
|
+
|
|
234
|
+
`verification-environment-changed` is an unavailable environment, not a product regression.
|
|
235
|
+
Installed dependency and runner content is bound to evidence; resume in a changed environment rebuilds baseline and revalidates retained phases.
|
|
236
|
+
Reuse compatible sealed candidates.
|
|
237
|
+
Do not edit product code to compensate for missing or drifting tools.
|
package/prompts/abel-init.md
CHANGED
|
@@ -3,14 +3,16 @@ description: Initialize or safely repair an Abel OpenSpec project
|
|
|
3
3
|
argument-hint: "[project-path]"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
This procedure applies only when the user explicitly invokes `/abel-init`.
|
|
7
|
+
Reading this file, mentioning the command, or finding OpenSpec artifacts does not activate it.
|
|
8
8
|
Read the complete value inside `<abel-request>` without tokenizing it a second time.
|
|
9
9
|
|
|
10
10
|
<abel-request>
|
|
11
11
|
$ARGUMENTS
|
|
12
12
|
</abel-request>
|
|
13
13
|
|
|
14
|
+
<!-- ABEL:PROMPT:abel-init -->
|
|
15
|
+
|
|
14
16
|
The request is an optional project path; when absent, use the current working directory.
|
|
15
17
|
Resolve and report the canonical target root before action.
|
|
16
18
|
|
|
@@ -34,6 +36,8 @@ Resolve and report the canonical target root before action.
|
|
|
34
36
|
5. Resolve and validate the selected schema and templates before claiming readiness.
|
|
35
37
|
6. Create or repair only verified `<!-- ABEL:AGENTS-INDEX:START -->` … `<!-- ABEL:AGENTS-INDEX:END -->` managed regions.
|
|
36
38
|
Preserve every byte of human content outside those markers and do not cross nested repositories.
|
|
39
|
+
Keep the index descriptive: file locations and ordinary project commands only; never instruct ordinary tasks to enter an Abel workflow.
|
|
40
|
+
Never record runtime/session ids, credentials, approval state, or dirty-state ledgers.
|
|
37
41
|
7. Re-run all probes after writes.
|
|
38
42
|
A second identical Init must produce no additional changes.
|
|
39
43
|
|
|
@@ -43,3 +47,6 @@ Do not probe `git-commit`, external `dev-browser`, or a dedicated time Skill; no
|
|
|
43
47
|
|
|
44
48
|
Report one final result with the root, selected toolchain, actions actually taken, OpenSpec capability/schema/template evidence, AGENTS files changed or unchanged, research-Skill paths or remediation, and `ready | partial | paused`.
|
|
45
49
|
Never report success from the initial probe when the post-write recheck did not pass.
|
|
50
|
+
|
|
51
|
+
Init ends with that report; later ordinary tasks use normal engineering behavior.
|
|
52
|
+
Never start another Abel stage, archive, publish, release, stage, or commit implicitly.
|
package/src/apply-transaction.ts
CHANGED
|
@@ -20,10 +20,12 @@ import {
|
|
|
20
20
|
import { tmpdir } from "node:os";
|
|
21
21
|
import path from "node:path";
|
|
22
22
|
import { DatabaseSync } from "node:sqlite";
|
|
23
|
-
|
|
24
23
|
import type { ArtifactStore } from "./artifact-store.ts";
|
|
24
|
+
import { compareCanonicalStrings } from "./canonical.ts";
|
|
25
25
|
import { isValidRelativePath } from "./contracts.ts";
|
|
26
26
|
import { observeSafePath } from "./safe-path.ts";
|
|
27
|
+
import { configureSqlite, ensureSqliteSchema } from "./sqlite-schema.ts";
|
|
28
|
+
import { APPLY_SCHEMA } from "./storage-schema.ts";
|
|
27
29
|
import type {
|
|
28
30
|
WorkspaceEntry,
|
|
29
31
|
WorkspaceRevision,
|
|
@@ -548,7 +550,11 @@ export async function verifyCumulativeRevision(
|
|
|
548
550
|
);
|
|
549
551
|
chmodSync(temporary, 0o700);
|
|
550
552
|
try {
|
|
551
|
-
input.workspaceStore.
|
|
553
|
+
await input.workspaceStore.materializeAsync(
|
|
554
|
+
revision.revisionId,
|
|
555
|
+
temporary,
|
|
556
|
+
input.signal,
|
|
557
|
+
);
|
|
552
558
|
if (input.signal?.aborted) {
|
|
553
559
|
return {
|
|
554
560
|
ok: false,
|
|
@@ -629,16 +635,8 @@ export class ApplyTransaction {
|
|
|
629
635
|
throw new Error("transaction-database-path-invalid");
|
|
630
636
|
}
|
|
631
637
|
this.#database = new DatabaseSync(databasePath);
|
|
632
|
-
this.#database
|
|
633
|
-
|
|
634
|
-
);
|
|
635
|
-
this.#database.exec(`
|
|
636
|
-
CREATE TABLE IF NOT EXISTS apply_transaction_journal (
|
|
637
|
-
transaction_id TEXT PRIMARY KEY,
|
|
638
|
-
journal_json TEXT NOT NULL,
|
|
639
|
-
journal_hash TEXT NOT NULL
|
|
640
|
-
) STRICT;
|
|
641
|
-
`);
|
|
638
|
+
configureSqlite(this.#database);
|
|
639
|
+
ensureSqliteSchema(this.#database, APPLY_SCHEMA);
|
|
642
640
|
chmodSync(databasePath, 0o600);
|
|
643
641
|
}
|
|
644
642
|
|
|
@@ -986,7 +984,7 @@ export class ApplyTransaction {
|
|
|
986
984
|
const deepestFirst = [...journal.createdDirectories].sort(
|
|
987
985
|
(left, right) =>
|
|
988
986
|
right.split("/").length - left.split("/").length ||
|
|
989
|
-
right
|
|
987
|
+
compareCanonicalStrings(right, left),
|
|
990
988
|
);
|
|
991
989
|
for (const relative of deepestFirst) {
|
|
992
990
|
const observation = observeSafePath(journal.consumerRoot, relative);
|
|
@@ -1244,7 +1242,11 @@ export class ApplyTransaction {
|
|
|
1244
1242
|
path.join(this.root, "post-apply-verification-"),
|
|
1245
1243
|
);
|
|
1246
1244
|
try {
|
|
1247
|
-
this.#workspaces.
|
|
1245
|
+
await this.#workspaces.materializeAsync(
|
|
1246
|
+
journal.finalRevisionId,
|
|
1247
|
+
isolatedRoot,
|
|
1248
|
+
signal,
|
|
1249
|
+
);
|
|
1248
1250
|
const execution = Promise.resolve(
|
|
1249
1251
|
this.#hooks.postApply({
|
|
1250
1252
|
transactionId,
|
package/src/artifact-store.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
chmodSync,
|
|
4
4
|
closeSync,
|
|
5
5
|
constants,
|
|
6
|
+
copyFileSync,
|
|
6
7
|
fsyncSync,
|
|
7
8
|
lstatSync,
|
|
8
9
|
mkdirSync,
|
|
@@ -160,6 +161,22 @@ export class ArtifactStore {
|
|
|
160
161
|
return Buffer.from(bytes);
|
|
161
162
|
}
|
|
162
163
|
|
|
164
|
+
/** Clone when supported; always use a distinct inode and verify the copied bytes. */
|
|
165
|
+
copyVerified(hash: string, destination: string): number {
|
|
166
|
+
const source = this.#blobPath(hash);
|
|
167
|
+
const stat = lstatSync(source, { throwIfNoEntry: false });
|
|
168
|
+
if (!stat?.isFile() || stat.isSymbolicLink())
|
|
169
|
+
throw new Error("artifact-unavailable");
|
|
170
|
+
copyFileSync(
|
|
171
|
+
source,
|
|
172
|
+
destination,
|
|
173
|
+
constants.COPYFILE_EXCL | constants.COPYFILE_FICLONE,
|
|
174
|
+
);
|
|
175
|
+
const bytes = readFileSync(destination);
|
|
176
|
+
if (digest(bytes) !== hash) throw new Error("artifact-integrity-invalid");
|
|
177
|
+
return bytes.byteLength;
|
|
178
|
+
}
|
|
179
|
+
|
|
163
180
|
referenceCount(hash: string): number {
|
|
164
181
|
requireHash(hash);
|
|
165
182
|
const referencePath = this.#referencePath(hash);
|
package/src/candidate-patch.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { lstatSync, readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { generateUnifiedPatch } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { compareCanonicalStrings } from "./canonical.ts";
|
|
4
5
|
|
|
5
6
|
import { isValidRelativePath } from "./contracts.ts";
|
|
6
7
|
import { observeSafePath } from "./safe-path.ts";
|
|
@@ -242,7 +243,7 @@ export function compileCandidatePatch(input: {
|
|
|
242
243
|
|
|
243
244
|
const diff = [...states.values()]
|
|
244
245
|
.filter((state) => state.base !== state.current)
|
|
245
|
-
.sort((left, right) => left.path
|
|
246
|
+
.sort((left, right) => compareCanonicalStrings(left.path, right.path))
|
|
246
247
|
.map(renderFileDiff)
|
|
247
248
|
.join("");
|
|
248
249
|
if (diff.length === 0) throw new Error("candidate-patch-empty");
|
package/src/canonical.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Canonical identity ordering is UTF-16 code-unit order, never host collation. */
|
|
2
|
+
export function compareCanonicalStrings(left: string, right: string): number {
|
|
3
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function canonicalJson(value: unknown): string {
|
|
7
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
8
|
+
if (value && typeof value === "object") {
|
|
9
|
+
return `{${Object.entries(value as Record<string, unknown>)
|
|
10
|
+
.filter(([, entry]) => entry !== undefined)
|
|
11
|
+
.sort(([left], [right]) => compareCanonicalStrings(left, right))
|
|
12
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`)
|
|
13
|
+
.join(",")}}`;
|
|
14
|
+
}
|
|
15
|
+
return JSON.stringify(value) ?? "null";
|
|
16
|
+
}
|