dorfl 0.12.0 → 0.13.1

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.
Files changed (46) hide show
  1. package/dist/advance.d.ts +8 -1
  2. package/dist/advance.d.ts.map +1 -1
  3. package/dist/advance.js +39 -6
  4. package/dist/advance.js.map +1 -1
  5. package/dist/apply-decide.d.ts +7 -1
  6. package/dist/apply-decide.d.ts.map +1 -1
  7. package/dist/apply-decide.js +23 -10
  8. package/dist/apply-decide.js.map +1 -1
  9. package/dist/apply-persist.d.ts +52 -8
  10. package/dist/apply-persist.d.ts.map +1 -1
  11. package/dist/apply-persist.js +78 -9
  12. package/dist/apply-persist.js.map +1 -1
  13. package/dist/do.d.ts.map +1 -1
  14. package/dist/do.js +71 -2
  15. package/dist/do.js.map +1 -1
  16. package/dist/frontmatter.d.ts +17 -10
  17. package/dist/frontmatter.d.ts.map +1 -1
  18. package/dist/frontmatter.js +3 -3
  19. package/dist/frontmatter.js.map +1 -1
  20. package/dist/pi-harness.d.ts +5 -0
  21. package/dist/pi-harness.d.ts.map +1 -1
  22. package/dist/pi-harness.js +93 -2
  23. package/dist/pi-harness.js.map +1 -1
  24. package/dist/protocol/WORK-CONTRACT.md +2 -1
  25. package/dist/reap-agent-tree.d.ts.map +1 -1
  26. package/dist/reap-agent-tree.js +19 -3
  27. package/dist/reap-agent-tree.js.map +1 -1
  28. package/dist/skills/setup/protocol/WORK-CONTRACT.md +2 -1
  29. package/dist/skills/triage-observations/SKILL.md +1 -1
  30. package/dist/triage-gate.d.ts +4 -2
  31. package/dist/triage-gate.d.ts.map +1 -1
  32. package/dist/triage-gate.js.map +1 -1
  33. package/dist/triage-persist.d.ts +55 -5
  34. package/dist/triage-persist.d.ts.map +1 -1
  35. package/dist/triage-persist.js +68 -7
  36. package/dist/triage-persist.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/advance.ts +52 -4
  39. package/src/apply-decide.ts +23 -10
  40. package/src/apply-persist.ts +86 -9
  41. package/src/do.ts +88 -2
  42. package/src/frontmatter.ts +17 -10
  43. package/src/pi-harness.ts +102 -2
  44. package/src/reap-agent-tree.ts +19 -3
  45. package/src/triage-gate.ts +4 -2
  46. package/src/triage-persist.ts +110 -9
@@ -1,4 +1,4 @@
1
- import {readFileSync} from 'node:fs';
1
+ import {readFileSync, writeFileSync} from 'node:fs';
2
2
  import {join} from 'node:path';
3
3
  import {run, type RunResult} from './git.js';
4
4
  import {workItemRel} from './work-layout.js';
@@ -25,8 +25,12 @@ import {setFrontmatterMarker} from './frontmatter.js';
25
25
  * 1. **the conservative auto-disposition** ({@link autoDispositionObservation},
26
26
  * US #17, `observationTriage: 'auto'`-gated): act on the no-question case on
27
27
  * the UNTRIAGED observation in ONE local commit. BOTH no-question cases now
28
- * DISCHARGE the redundant note BY DELETION (there is no resting `triaged:keep`
29
- * state any more — task `agentic-apply-retire-disposition-vocabulary`):
28
+ * DISCHARGE the redundant note BY DELETION — an AUTO disposition never RESTS a
29
+ * note, because both of its cases (duplicate / map) are by definition notes
30
+ * that carry no unique signal (task `agentic-apply-retire-disposition-
31
+ * vocabulary`; the ONE case that does rest a note is the human-answered
32
+ * `resolve` verdict on the APPLY rung, see ADR `resolve-settles-the-question-
33
+ * loop-not-the-note`):
30
34
  * - `duplicate` → `git rm` the duplicate in a standalone commit, the
31
35
  * duplicated-of identity + reason in the commit message (git history =
32
36
  * archive). A duplicate is a redundant copy of an already-captured signal,
@@ -34,7 +38,6 @@ import {setFrontmatterMarker} from './frontmatter.js';
34
38
  * - `map` → the note is already covered by the existing item it maps onto, so
35
39
  * it is settled — `git rm` it in a standalone commit, the mapped-onto
36
40
  * identity + reason recorded in the commit message (mirroring `duplicate`).
37
- * There is no resting `triaged:keep` note any more.
38
41
  * 2. **promote → SELF-CONTAINED new-item creation + DELETE through the CAS**
39
42
  * ({@link promoteObservation}, US #1/#3/#8): an ANSWERED "promote" drafts a
40
43
  * new `work/tasks/ready/<new-slug>.md` whose body is built FROM the
@@ -120,6 +123,102 @@ export class TriagePersistError extends Error {
120
123
  }
121
124
  }
122
125
 
126
+ // --- Back-stamping a KEPT note that was resolved before the marker existed ---
127
+
128
+ export interface StampTriagedMarkerOptions {
129
+ /** Working clone/worktree the stamp commits in. */
130
+ cwd: string;
131
+ /** The namespaced observation identity (`observation:<slug>`). */
132
+ item: string;
133
+ /** The observation file path RELATIVE to `cwd`. */
134
+ itemPath: string;
135
+ /** The `triaged:` value to stamp (the apply rung's settled disposition). */
136
+ value: string;
137
+ /** Why the stamp is being back-filled (rides the commit message). */
138
+ reason: string;
139
+ /** Advisory committer id for the commit subject. Defaults to git user.name. */
140
+ by?: string;
141
+ /** Environment for child git processes. */
142
+ env?: NodeJS.ProcessEnv;
143
+ /** Sink for human-readable progress notes. */
144
+ note?: (message: string) => void;
145
+ }
146
+
147
+ export interface StampTriagedMarkerResult {
148
+ /** The commit sha the stamp produced, or `undefined` when the body was already marked. */
149
+ commit?: string;
150
+ /** A human-readable summary. */
151
+ message: string;
152
+ }
153
+
154
+ /**
155
+ * Stamp the `triaged:` settled marker onto an observation that was ALREADY
156
+ * resolved-and-kept, in ONE local commit — the SELF-HEALING back-fill for notes
157
+ * resolved BEFORE the apply rung learned to stamp it (ADR
158
+ * `resolve-settles-the-question-loop-not-the-note`).
159
+ *
160
+ * Why a back-fill exists at all: the fix that makes `resolve` stamp the marker is
161
+ * forward-only, so every note a repo resolved under the old code still rests
162
+ * `needsAnswers:false` + no sidecar + NO marker — the exact shape the triage rung
163
+ * re-asks. Those notes are not hypothetical (a single `rocketh` cycle re-asked
164
+ * four, with eleven more queued behind them), and they are un-fixable from the
165
+ * human's side: answering again just resolves again into the same shape.
166
+ *
167
+ * The trigger is PROOF, not a heuristic: the caller fires this only when the body
168
+ * carries the ENGINE-WRITTEN `## Applied answers` record, which only
169
+ * `apply-persist.ts` writes and only after a human answered every open question.
170
+ * A hand-written note cannot accidentally acquire it, and the stamp is exactly
171
+ * what the apply rung WOULD have written had it known how.
172
+ *
173
+ * ONE commit touching ONE file, so the tree-less publish (`pushTreelessResult`)
174
+ * carries it to the arbiter like any other triage-rung marker write; once landed
175
+ * the note drops out of the triage pool and this path is never taken again.
176
+ */
177
+ export function stampTriagedMarker(
178
+ options: StampTriagedMarkerOptions,
179
+ ): StampTriagedMarkerResult {
180
+ const {cwd, item, itemPath, value, reason, env} = options;
181
+ const note = options.note ?? (() => {});
182
+
183
+ if (gitSoft(['rev-parse', '--git-dir'], cwd, env).status !== 0) {
184
+ throw new TriagePersistError('not inside a git repository');
185
+ }
186
+
187
+ const abs = join(cwd, itemPath);
188
+ const before = readFileSync(abs, 'utf8');
189
+ const after = setFrontmatterMarker(before, 'triaged', value);
190
+ if (after === before) {
191
+ // Already marked (or a malformed fence `setFrontmatterMarker` refuses to
192
+ // rewrite) — nothing to commit; never produce an empty commit.
193
+ const message = `triage ${item}: already carries triaged:${value} — no stamp needed.`;
194
+ note(message);
195
+ return {message};
196
+ }
197
+ writeFileSync(abs, after);
198
+ const by = options.by || resolveBy(cwd, env);
199
+ gitHard(['add', '--', itemPath], cwd, env);
200
+ const subject = `advance: triage ${item} → settled (by ${by})`;
201
+ gitHard(
202
+ [
203
+ 'commit',
204
+ '--quiet',
205
+ '-m',
206
+ subject,
207
+ '-m',
208
+ `triaged: ${value}\n\n${reason}`,
209
+ ],
210
+ cwd,
211
+ env,
212
+ );
213
+ const commit = gitHard(['rev-parse', 'HEAD'], cwd, env).stdout.trim();
214
+ const message =
215
+ `triage ${item}: back-stamped triaged:${value} (the note was already ` +
216
+ 'resolved-and-kept — it carries an engine-written applied-answers record) ' +
217
+ 'so the triage rung stops re-asking its settled question.';
218
+ note(message);
219
+ return {commit, message};
220
+ }
221
+
123
222
  // --- The conservative auto-disposition (US #17) ---------------------------
124
223
 
125
224
  export interface AutoDispositionOptions {
@@ -149,8 +248,10 @@ export interface AutoDispositionResult {
149
248
  * DELETION (`git rm` in a standalone commit, the reason in the message). A
150
249
  * `duplicate` is a redundant copy of `existing`; a `map` is already covered by
151
250
  * `existing` — either way the note carries no unique signal, so it leaves the
152
- * inbox by being gone. There is no resting `triaged:keep` state any more (task
153
- * `agentic-apply-retire-disposition-vocabulary`). NEVER an auto-delete of a
251
+ * inbox by being gone. An AUTO disposition never RESTS a note (task
252
+ * `agentic-apply-retire-disposition-vocabulary`); resting a KEPT note is the
253
+ * human-answered `resolve` verdict's job on the apply rung (ADR
254
+ * `resolve-settles-the-question-loop-not-the-note`). NEVER an auto-delete of a
154
255
  * NON-redundant signal.
155
256
  */
156
257
  outcome: 'deleted';
@@ -167,9 +268,9 @@ export interface AutoDispositionResult {
167
268
  * ONE local commit, no question surfaced. BOTH no-question cases DISCHARGE the
168
269
  * redundant note BY DELETION (`git rm` in a STANDALONE commit, the mapped/
169
270
  * duplicated-of identity + reason in the commit message; git history = archive).
170
- * There is no resting `triaged:keep` state any more (task
171
- * `agentic-apply-retire-disposition-vocabulary` — a signal is still-open,
172
- * acted-on, or deleted):
271
+ * An AUTO disposition never RESTS a note (task
272
+ * `agentic-apply-retire-disposition-vocabulary`): its two cases carry no unique
273
+ * signal, so they are still-open, acted-on, or deleted — never kept:
173
274
  *
174
275
  * - `duplicate` → the note is an EXACT duplicate of `existing` (already
175
276
  * captured); the original carries the signal, so the copy is deleted; or