dorfl 0.1.2 → 0.2.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.
- package/dist/advance-drivers.d.ts +1 -1
- package/dist/advance-drivers.d.ts.map +1 -1
- package/dist/advance-drivers.js +15 -3
- package/dist/advance-drivers.js.map +1 -1
- package/dist/advance-lifecycle-template.d.ts.map +1 -1
- package/dist/advance-lifecycle-template.js +76 -4
- package/dist/advance-lifecycle-template.js.map +1 -1
- package/dist/advance-treeless-publish.d.ts +23 -0
- package/dist/advance-treeless-publish.d.ts.map +1 -1
- package/dist/advance-treeless-publish.js +41 -0
- package/dist/advance-treeless-publish.js.map +1 -1
- package/dist/advance.d.ts +59 -7
- package/dist/advance.d.ts.map +1 -1
- package/dist/advance.js +389 -82
- package/dist/advance.js.map +1 -1
- package/dist/advancing-lock.d.ts +30 -2
- package/dist/advancing-lock.d.ts.map +1 -1
- package/dist/advancing-lock.js +50 -0
- package/dist/advancing-lock.js.map +1 -1
- package/dist/agent-launch.d.ts +12 -0
- package/dist/agent-launch.d.ts.map +1 -1
- package/dist/agent-launch.js +22 -12
- package/dist/agent-launch.js.map +1 -1
- package/dist/agent-stop.d.ts +40 -2
- package/dist/agent-stop.d.ts.map +1 -1
- package/dist/agent-stop.js +30 -2
- package/dist/agent-stop.js.map +1 -1
- package/dist/apply-decide.d.ts +17 -3
- package/dist/apply-decide.d.ts.map +1 -1
- package/dist/apply-decide.js +36 -7
- package/dist/apply-decide.js.map +1 -1
- package/dist/apply-merge-action.d.ts +17 -5
- package/dist/apply-merge-action.d.ts.map +1 -1
- package/dist/apply-merge-action.js +43 -8
- package/dist/apply-merge-action.js.map +1 -1
- package/dist/apply-persist.d.ts +57 -26
- package/dist/apply-persist.d.ts.map +1 -1
- package/dist/apply-persist.js +166 -37
- package/dist/apply-persist.js.map +1 -1
- package/dist/apply-stuck-action.d.ts +151 -0
- package/dist/apply-stuck-action.d.ts.map +1 -0
- package/dist/apply-stuck-action.js +125 -0
- package/dist/apply-stuck-action.js.map +1 -0
- package/dist/brand.d.ts +12 -1
- package/dist/brand.d.ts.map +1 -1
- package/dist/brand.js +2 -1
- package/dist/brand.js.map +1 -1
- package/dist/cli-spinner.d.ts +1 -1
- package/dist/cli-spinner.js +1 -1
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +320 -63
- package/dist/cli.js.map +1 -1
- package/dist/close-job-template.js +1 -1
- package/dist/complete.d.ts +6 -3
- package/dist/complete.d.ts.map +1 -1
- package/dist/complete.js +91 -37
- package/dist/complete.js.map +1 -1
- package/dist/config.d.ts +55 -9
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +56 -4
- package/dist/config.js.map +1 -1
- package/dist/continue-branch.d.ts.map +1 -1
- package/dist/continue-branch.js +22 -0
- package/dist/continue-branch.js.map +1 -1
- package/dist/decision-engine.d.ts +38 -10
- package/dist/decision-engine.d.ts.map +1 -1
- package/dist/decision-engine.js +10 -6
- package/dist/decision-engine.js.map +1 -1
- package/dist/do-autopick.d.ts.map +1 -1
- package/dist/do-autopick.js +11 -1
- package/dist/do-autopick.js.map +1 -1
- package/dist/do-config.d.ts +1 -1
- package/dist/do.d.ts +49 -56
- package/dist/do.d.ts.map +1 -1
- package/dist/do.js +419 -30
- package/dist/do.js.map +1 -1
- package/dist/env-config.d.ts.map +1 -1
- package/dist/env-config.js +12 -4
- package/dist/env-config.js.map +1 -1
- package/dist/failure-cause.d.ts +3 -2
- package/dist/failure-cause.d.ts.map +1 -1
- package/dist/failure-cause.js +28 -1
- package/dist/failure-cause.js.map +1 -1
- package/dist/format.d.ts +6 -6
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +10 -27
- package/dist/format.js.map +1 -1
- package/dist/frontmatter.d.ts +16 -4
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js +27 -1
- package/dist/frontmatter.js.map +1 -1
- package/dist/gc.d.ts +65 -6
- package/dist/gc.d.ts.map +1 -1
- package/dist/gc.js +126 -9
- package/dist/gc.js.map +1 -1
- package/dist/github.d.ts +14 -0
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +73 -0
- package/dist/github.js.map +1 -1
- package/dist/harness.d.ts +25 -0
- package/dist/harness.d.ts.map +1 -1
- package/dist/harness.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/install-ci-branch-protection.d.ts +132 -38
- package/dist/install-ci-branch-protection.d.ts.map +1 -1
- package/dist/install-ci-branch-protection.js +190 -48
- package/dist/install-ci-branch-protection.js.map +1 -1
- package/dist/install-ci-core.d.ts +21 -1
- package/dist/install-ci-core.d.ts.map +1 -1
- package/dist/install-ci-core.js +10 -3
- package/dist/install-ci-core.js.map +1 -1
- package/dist/install-ci-github.d.ts +32 -0
- package/dist/install-ci-github.d.ts.map +1 -1
- package/dist/install-ci-github.js +73 -0
- package/dist/install-ci-github.js.map +1 -1
- package/dist/install-skills.d.ts +123 -0
- package/dist/install-skills.d.ts.map +1 -0
- package/dist/install-skills.js +100 -0
- package/dist/install-skills.js.map +1 -0
- package/dist/intake-trigger-template.js +1 -1
- package/dist/intake.d.ts +17 -4
- package/dist/intake.d.ts.map +1 -1
- package/dist/intake.js +23 -7
- package/dist/intake.js.map +1 -1
- package/dist/integration-core.d.ts +10 -7
- package/dist/integration-core.d.ts.map +1 -1
- package/dist/integration-core.js +174 -60
- package/dist/integration-core.js.map +1 -1
- package/dist/integrator.d.ts +1 -1
- package/dist/integrator.d.ts.map +1 -1
- package/dist/integrator.js +19 -3
- package/dist/integrator.js.map +1 -1
- package/dist/isolation.d.ts +2 -2
- package/dist/isolation.d.ts.map +1 -1
- package/dist/isolation.js +9 -0
- package/dist/isolation.js.map +1 -1
- package/dist/item-lock.d.ts +196 -129
- package/dist/item-lock.d.ts.map +1 -1
- package/dist/item-lock.js +329 -254
- package/dist/item-lock.js.map +1 -1
- package/dist/ledger-write.d.ts +31 -26
- package/dist/ledger-write.d.ts.map +1 -1
- package/dist/ledger-write.js +100 -122
- package/dist/ledger-write.js.map +1 -1
- package/dist/lifecycle-gather.d.ts +17 -0
- package/dist/lifecycle-gather.d.ts.map +1 -1
- package/dist/lifecycle-gather.js +7 -1
- package/dist/lifecycle-gather.js.map +1 -1
- package/dist/lifecycle-pools.d.ts +40 -3
- package/dist/lifecycle-pools.d.ts.map +1 -1
- package/dist/lifecycle-pools.js +26 -6
- package/dist/lifecycle-pools.js.map +1 -1
- package/dist/merge-question-surfacer.d.ts +15 -1
- package/dist/merge-question-surfacer.d.ts.map +1 -1
- package/dist/merge-question-surfacer.js +18 -3
- package/dist/merge-question-surfacer.js.map +1 -1
- package/dist/migrate-stuck-locks.d.ts +129 -0
- package/dist/migrate-stuck-locks.d.ts.map +1 -0
- package/dist/migrate-stuck-locks.js +355 -0
- package/dist/migrate-stuck-locks.js.map +1 -0
- package/dist/needs-attention.d.ts +293 -16
- package/dist/needs-attention.d.ts.map +1 -1
- package/dist/needs-attention.js +532 -57
- package/dist/needs-attention.js.map +1 -1
- package/dist/orphan-sidecar.d.ts +8 -4
- package/dist/orphan-sidecar.d.ts.map +1 -1
- package/dist/orphan-sidecar.js +35 -2
- package/dist/orphan-sidecar.js.map +1 -1
- package/dist/pi-harness.d.ts +16 -0
- package/dist/pi-harness.d.ts.map +1 -1
- package/dist/pi-harness.js +82 -2
- package/dist/pi-harness.js.map +1 -1
- package/dist/placement.d.ts +3 -3
- package/dist/placement.js +1 -1
- package/dist/prd-to-spec.d.ts.map +1 -1
- package/dist/prd-to-spec.js +9 -5
- package/dist/prd-to-spec.js.map +1 -1
- package/dist/protocol/CLAIM-PROTOCOL.md +17 -10
- package/dist/protocol/REVIEW-PROTOCOL.md +4 -1
- package/dist/protocol/SURFACE-PROTOCOL.md +16 -2
- package/dist/protocol/TASKING-PROTOCOL.md +3 -1
- package/dist/protocol/WORK-CONTRACT.md +22 -18
- package/dist/protocol/task-template.md +1 -1
- package/dist/reap-branches.d.ts +12 -9
- package/dist/reap-branches.d.ts.map +1 -1
- package/dist/reap-branches.js +25 -7
- package/dist/reap-branches.js.map +1 -1
- package/dist/recover-isolated.d.ts +12 -0
- package/dist/recover-isolated.d.ts.map +1 -1
- package/dist/recover-isolated.js +5 -0
- package/dist/recover-isolated.js.map +1 -1
- package/dist/repo-config.d.ts +23 -2
- package/dist/repo-config.d.ts.map +1 -1
- package/dist/repo-config.js +53 -7
- package/dist/repo-config.js.map +1 -1
- package/dist/repo-mirror.d.ts.map +1 -1
- package/dist/repo-mirror.js +18 -2
- package/dist/repo-mirror.js.map +1 -1
- package/dist/review-verdict.js +1 -1
- package/dist/review-verdict.js.map +1 -1
- package/dist/run.d.ts +1 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +54 -19
- package/dist/run.js.map +1 -1
- package/dist/scan.d.ts +17 -8
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +22 -8
- package/dist/scan.js.map +1 -1
- package/dist/select-priority.d.ts +2 -2
- package/dist/select-priority.js +1 -1
- package/dist/select-priority.js.map +1 -1
- package/dist/sidecar-apply.js +1 -1
- package/dist/sidecar-apply.js.map +1 -1
- package/dist/sidecar.d.ts +45 -10
- package/dist/sidecar.d.ts.map +1 -1
- package/dist/sidecar.js +82 -2
- package/dist/sidecar.js.map +1 -1
- package/dist/skills/answer-questions/SKILL.md +89 -0
- package/dist/skills/capture-signal/SKILL.md +52 -0
- package/dist/skills/convert-from-prd-to-spec/SKILL.md +90 -0
- package/dist/skills/drive-tasks/SKILL.md +218 -0
- package/dist/skills/from-idea/SKILL.md +83 -0
- package/dist/skills/merge-prs/SKILL.md +70 -0
- package/dist/skills/orchestrate/SKILL.md +101 -0
- package/dist/skills/promote/SKILL.md +35 -0
- package/dist/skills/review/SKILL.md +16 -0
- package/dist/skills/setup/SKILL.md +258 -0
- package/dist/skills/setup/protocol/ADR-FORMAT.md +47 -0
- package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +224 -0
- package/dist/skills/setup/protocol/REVIEW-PROTOCOL.md +122 -0
- package/dist/skills/setup/protocol/SURFACE-PROTOCOL.md +135 -0
- package/dist/skills/setup/protocol/TASKING-PROTOCOL.md +124 -0
- package/dist/skills/setup/protocol/WORK-CONTRACT.md +280 -0
- package/dist/skills/setup/protocol/spec-template.md +71 -0
- package/dist/skills/setup/protocol/task-template.md +65 -0
- package/dist/skills/surface-questions/SKILL.md +16 -0
- package/dist/skills/to-spec/SKILL.md +34 -0
- package/dist/skills/to-task/SKILL.md +19 -0
- package/dist/skills/triage-observations/SKILL.md +78 -0
- package/dist/skills/work/SKILL.md +51 -0
- package/dist/slug-namespace.d.ts +5 -5
- package/dist/slug-namespace.js +5 -5
- package/dist/slug-namespace.js.map +1 -1
- package/dist/spec-complete.d.ts.map +1 -1
- package/dist/spec-complete.js +2 -3
- package/dist/spec-complete.js.map +1 -1
- package/dist/start.d.ts.map +1 -1
- package/dist/start.js +51 -57
- package/dist/start.js.map +1 -1
- package/dist/surface-gate.d.ts.map +1 -1
- package/dist/surface-gate.js +10 -3
- package/dist/surface-gate.js.map +1 -1
- package/dist/surface-persist.js +1 -1
- package/dist/surface-persist.js.map +1 -1
- package/dist/tasker-review-loop.d.ts +4 -3
- package/dist/tasker-review-loop.d.ts.map +1 -1
- package/dist/tasker-review-loop.js.map +1 -1
- package/dist/tasking-lock.d.ts +2 -2
- package/dist/tasking-lock.d.ts.map +1 -1
- package/dist/tasking-lock.js +53 -32
- package/dist/tasking-lock.js.map +1 -1
- package/dist/tasking.d.ts +40 -6
- package/dist/tasking.d.ts.map +1 -1
- package/dist/tasking.js +224 -12
- package/dist/tasking.js.map +1 -1
- package/dist/triage-persist.d.ts +10 -6
- package/dist/triage-persist.d.ts.map +1 -1
- package/dist/triage-persist.js +34 -4
- package/dist/triage-persist.js.map +1 -1
- package/dist/vendor/incur/agents.d.ts +58 -0
- package/dist/vendor/incur/agents.d.ts.map +1 -0
- package/dist/vendor/incur/agents.js +343 -0
- package/dist/vendor/incur/agents.js.map +1 -0
- package/dist/verify-workflow-template.js +1 -1
- package/dist/watch-session.d.ts +11 -3
- package/dist/watch-session.d.ts.map +1 -1
- package/dist/watch-session.js +94 -7
- package/dist/watch-session.js.map +1 -1
- package/dist/work-layout.d.ts +7 -3
- package/dist/work-layout.d.ts.map +1 -1
- package/dist/work-layout.js +6 -3
- package/dist/work-layout.js.map +1 -1
- package/dist/workspace.d.ts +1 -1
- package/package.json +2 -2
- package/src/advance-drivers.ts +16 -3
- package/src/advance-lifecycle-template.ts +89 -4
- package/src/advance-treeless-publish.ts +45 -0
- package/src/advance.ts +467 -87
- package/src/advancing-lock.ts +96 -1
- package/src/agent-launch.ts +37 -12
- package/src/agent-stop.ts +60 -2
- package/src/apply-decide.ts +36 -7
- package/src/apply-merge-action.ts +45 -7
- package/src/apply-persist.ts +228 -54
- package/src/apply-stuck-action.ts +260 -0
- package/src/brand.ts +14 -2
- package/src/cli-spinner.ts +1 -1
- package/src/cli.ts +401 -70
- package/src/close-job-template.ts +1 -1
- package/src/complete.ts +110 -52
- package/src/config.ts +107 -13
- package/src/continue-branch.ts +22 -0
- package/src/decision-engine.ts +55 -17
- package/src/do-autopick.ts +12 -1
- package/src/do-config.ts +1 -1
- package/src/do.ts +511 -38
- package/src/env-config.ts +12 -4
- package/src/failure-cause.ts +30 -1
- package/src/format.ts +10 -30
- package/src/frontmatter.ts +35 -5
- package/src/gc.ts +172 -9
- package/src/github.ts +78 -0
- package/src/harness.ts +25 -0
- package/src/index.ts +10 -0
- package/src/install-ci-branch-protection.ts +282 -57
- package/src/install-ci-core.ts +30 -3
- package/src/install-ci-github.ts +86 -0
- package/src/install-skills.ts +166 -0
- package/src/intake-trigger-template.ts +1 -1
- package/src/intake.ts +46 -11
- package/src/integration-core.ts +199 -65
- package/src/integrator.ts +22 -9
- package/src/isolation.ts +11 -2
- package/src/item-lock.ts +485 -341
- package/src/ledger-write.ts +135 -150
- package/src/lifecycle-gather.ts +29 -6
- package/src/lifecycle-pools.ts +67 -9
- package/src/merge-question-surfacer.ts +28 -4
- package/src/migrate-stuck-locks.ts +451 -0
- package/src/needs-attention.ts +857 -84
- package/src/orphan-sidecar.ts +47 -6
- package/src/pi-harness.ts +82 -2
- package/src/placement.ts +3 -3
- package/src/prd-to-spec.ts +13 -5
- package/src/reap-branches.ts +38 -14
- package/src/recover-isolated.ts +17 -0
- package/src/repo-config.ts +55 -6
- package/src/repo-mirror.ts +23 -1
- package/src/review-verdict.ts +1 -1
- package/src/run.ts +64 -18
- package/src/scan.ts +22 -8
- package/src/select-priority.ts +3 -3
- package/src/sidecar-apply.ts +1 -1
- package/src/sidecar.ts +138 -11
- package/src/slug-namespace.ts +7 -7
- package/src/spec-complete.ts +2 -3
- package/src/start.ts +53 -59
- package/src/surface-gate.ts +10 -3
- package/src/surface-persist.ts +1 -1
- package/src/tasker-review-loop.ts +4 -3
- package/src/tasking-lock.ts +52 -36
- package/src/tasking.ts +261 -17
- package/src/triage-persist.ts +56 -12
- package/src/vendor/incur/LICENSE +21 -0
- package/src/vendor/incur/README.md +19 -0
- package/src/vendor/incur/agents.ts +392 -0
- package/src/verify-workflow-template.ts +1 -1
- package/src/watch-session.ts +102 -7
- package/src/work-layout.ts +6 -3
- package/src/workspace.ts +1 -1
package/src/tasking.ts
CHANGED
|
@@ -53,7 +53,7 @@ import type {ReviewGate} from './review-gate.js';
|
|
|
53
53
|
* (`tasking-eligibility.ts`) and the tasking LOCK (`tasking-lock.ts`) together to
|
|
54
54
|
* task a spec into `work/tasks/backlog/` STAGED items (task
|
|
55
55
|
* `pre-backlog-staging-folder-and-promote-step-a` — the runner-owned promotion
|
|
56
|
-
* moves them `
|
|
56
|
+
* moves them `tasks/backlog/ → tasks/ready/` later), with the RUNNER owning every git-state
|
|
57
57
|
* transition. This is the spec branch of the `do` worker (ADR
|
|
58
58
|
* `command-surface-and-journeys.md` §3/§3a), NOT a standalone `task` command;
|
|
59
59
|
* `do.ts` dispatches `resolved.namespace === 'spec'` here.
|
|
@@ -244,6 +244,18 @@ export interface PerformTaskOptions {
|
|
|
244
244
|
* loop's {@link taskerLoopModel} — see the note there.
|
|
245
245
|
*/
|
|
246
246
|
acceptanceReviewModel?: string;
|
|
247
|
+
/**
|
|
248
|
+
* **The cross-job merge-serialiser CAS-retry cap** (config `mergeRetries`, spec
|
|
249
|
+
* `land-time-reverify-and-parallel-merge-ceiling` Story 5 / Applied Answer q1
|
|
250
|
+
* (a)). Threaded VERBATIM into {@link performIntegration} so a wide-matrix CI's
|
|
251
|
+
* raised cap actually reaches the cross-job land queue (the CAS loop IS the
|
|
252
|
+
* queue across separate jobs) on the `do spec:<slug>` tasking-transition path
|
|
253
|
+
* too. Resolved ONCE at the entry point through the gate-family precedence chain
|
|
254
|
+
* (flag > env > per-repo > global > default), same as `complete`/`do`/`run`.
|
|
255
|
+
* Unset ⇒ falls through to the engine's `DEFAULT_MERGE_RETRIES = 1000`
|
|
256
|
+
* (byte-for-byte unchanged).
|
|
257
|
+
*/
|
|
258
|
+
mergeRetries?: number;
|
|
247
259
|
/** Injectable lock seam (tests stub acquire/release). Defaults to the real CAS. */
|
|
248
260
|
lock?: TaskingLockSeam;
|
|
249
261
|
/**
|
|
@@ -255,10 +267,10 @@ export interface PerformTaskOptions {
|
|
|
255
267
|
* resolver (`src/placement.ts`). The resolver overlays an EXPLICIT operator
|
|
256
268
|
* flag ({@link explicitTasksLandIn}, top) and the UNTRUSTED-ORIGIN force
|
|
257
269
|
* (`originTrust: untrusted` ⇒ staging) on top. Unset ⇒ the resolver's
|
|
258
|
-
* built-in floor applies (`staging` = `
|
|
270
|
+
* built-in floor applies (`staging` = `tasks/backlog/`, the conservative
|
|
259
271
|
* landing that preserves zero behaviour change for the normal path).
|
|
260
272
|
*/
|
|
261
|
-
tasksLandIn?: '
|
|
273
|
+
tasksLandIn?: 'backlog' | 'ready';
|
|
262
274
|
/**
|
|
263
275
|
* **The OPERATOR's EXPLICIT task-placement override** (the TOP precedence
|
|
264
276
|
* rung). When set, the runner-deterministic resolver lands the tasks HERE
|
|
@@ -268,7 +280,7 @@ export interface PerformTaskOptions {
|
|
|
268
280
|
* force-key"). Set ONLY when the operator typed `--tasks-land-in <where>`;
|
|
269
281
|
* never when the value came from config.
|
|
270
282
|
*/
|
|
271
|
-
explicitTasksLandIn?: '
|
|
283
|
+
explicitTasksLandIn?: 'backlog' | 'ready';
|
|
272
284
|
/**
|
|
273
285
|
* **The tasker review→edit→converge LOOP** (`slicer-review-edit-loop`, GATES spec
|
|
274
286
|
* `work/specs/ready/review.md` RESOLVED DESIGN — Shape 2 / insertion point A). When
|
|
@@ -322,10 +334,10 @@ const DEFAULT_ARBITER = 'origin';
|
|
|
322
334
|
* task `pre-backlog-staging-folder-and-promote-step-a`, governing ADR
|
|
323
335
|
* `placement-is-runner-deterministic-humanonly-is-agent-judgement`). The runner
|
|
324
336
|
* lands the tasker's emitted task files HERE, NOT in `work/tasks/ready/`: an item
|
|
325
|
-
* born in `
|
|
337
|
+
* born in `tasks/backlog/` is durable + readable but NOT in the agent-eligible
|
|
326
338
|
* pool (`work/tasks/ready/` STILL means the pool — every reader is byte-for-byte
|
|
327
339
|
* unchanged). A runner/human-owned promotion (`promoteFromPreBacklog` in
|
|
328
|
-
* `needs-attention.ts`) moves an approved item `
|
|
340
|
+
* `needs-attention.ts`) moves an approved item `tasks/backlog/ → tasks/ready/` to make
|
|
329
341
|
* it claimable. STEP A: ADDITIVE — no `work/tasks/ready/` reader changes here.
|
|
330
342
|
*/
|
|
331
343
|
export const STAGED_TASKS_DIR = workFolderRel('tasks-backlog');
|
|
@@ -348,17 +360,19 @@ const TASK_PLACEMENT_SLOTS = {
|
|
|
348
360
|
} as const;
|
|
349
361
|
|
|
350
362
|
/**
|
|
351
|
-
* Map the `tasksLandIn` value spelling (`
|
|
363
|
+
* Map the `tasksLandIn` value spelling (`backlog` | `ready`) onto the
|
|
352
364
|
* resolver's lifecycle-generic side enum (`staging` | `pool`). Returns
|
|
353
365
|
* `undefined` when no value is set, so the resolver's next precedence rung
|
|
354
|
-
* applies (the built-in floor). The legacy `'backlog'`/`'todo'`
|
|
355
|
-
* are NOT accepted (clean break — the value was renamed
|
|
356
|
-
* → `'
|
|
366
|
+
* applies (the built-in floor). The legacy `'pre-backlog'`/`'todo'` spellings
|
|
367
|
+
* are NOT accepted (clean break — the staging value was renamed
|
|
368
|
+
* `'pre-backlog'` → `'backlog'`, and the pool value was renamed
|
|
369
|
+
* `'backlog'` → `'todo'` → `'ready'`, ADR
|
|
370
|
+
* `rename-task-pool-folder-todo-to-ready`).
|
|
357
371
|
*/
|
|
358
372
|
function landingToSide(
|
|
359
|
-
landing: '
|
|
373
|
+
landing: 'backlog' | 'ready' | undefined,
|
|
360
374
|
): 'staging' | 'pool' | undefined {
|
|
361
|
-
if (landing === '
|
|
375
|
+
if (landing === 'backlog') return 'staging';
|
|
362
376
|
if (landing === 'ready') return 'pool';
|
|
363
377
|
return undefined;
|
|
364
378
|
}
|
|
@@ -661,6 +675,12 @@ export async function performTask(
|
|
|
661
675
|
reviewGate: options.reviewGate,
|
|
662
676
|
reviewModel: options.acceptanceReviewModel,
|
|
663
677
|
reviewMaxRounds: 1,
|
|
678
|
+
// The cross-job merge-serialiser CAS-retry cap (config `mergeRetries`) —
|
|
679
|
+
// threaded so a wide-matrix CI's raised cap reaches the tasking-
|
|
680
|
+
// transition's land tail too (task
|
|
681
|
+
// `thread-merge-retries-cross-task-and-ratify-default`). Unset ⇒ falls
|
|
682
|
+
// through to the engine default (byte-for-byte unchanged).
|
|
683
|
+
mergeRetries: options.mergeRetries,
|
|
664
684
|
// The EXPLICITLY-chosen integrate mode proceeds AS-IS on an APPROVE — a
|
|
665
685
|
// `--merge` tasking run lands on main, `--propose` opens a PR. The tasking
|
|
666
686
|
// path's merge-vs-propose decision is the `integration` mode the user typed;
|
|
@@ -670,6 +690,17 @@ export async function performTask(
|
|
|
670
690
|
mode: options.integration ?? 'propose',
|
|
671
691
|
noPR: options.noPR,
|
|
672
692
|
providerInstance: options.providerInstance,
|
|
693
|
+
// PROPOSE-MODE PR BODY (task `slicing-pr-body-summary-threading`): mirror the
|
|
694
|
+
// BUILD path's body threading (`do.ts` — `body: agent.output`). The tasker
|
|
695
|
+
// agent produces no `LaunchResult.output` we can carry, but the slice-SET
|
|
696
|
+
// itself is a summary-worthy artifact: the emitted task slugs+titles, their
|
|
697
|
+
// coverage of the spec's user stories, the dependency graph (keystone +
|
|
698
|
+
// blockedBy edges), and any `needsAnswers` open questions the tasker review
|
|
699
|
+
// loop flagged. Compose it here and thread it into the shared core as `body`
|
|
700
|
+
// so the propose-mode PR carries the summary instead of degrading to
|
|
701
|
+
// `gh pr create --fill`. Empty-set (no emitted tasks) ⇒ undefined ⇒ the
|
|
702
|
+
// provider's `--fill` fallback (no regression on that edge).
|
|
703
|
+
body: composeTaskingProposeBody(slug, emitTasks),
|
|
673
704
|
type: 'tasking',
|
|
674
705
|
lifecycle: {
|
|
675
706
|
// Read the PR title / commit summary from the held spec (before it moves).
|
|
@@ -1058,7 +1089,7 @@ async function stageTaskingLifecycle(params: {
|
|
|
1058
1089
|
* snapshot (the branch-base state of `work/tasks/ready/`, taken BEFORE the agent
|
|
1059
1090
|
* ran), any new file is removed from the worktree and any changed file is
|
|
1060
1091
|
* checked back out to HEAD — so the subsequent `git add -A` cannot land it. The
|
|
1061
|
-
* runner's commit then carries ONLY the explicit `
|
|
1092
|
+
* runner's commit then carries ONLY the explicit `tasks/backlog/` placement.
|
|
1062
1093
|
*/
|
|
1063
1094
|
async function scrubPoolDrift(
|
|
1064
1095
|
cwd: string,
|
|
@@ -1122,8 +1153,9 @@ async function gitHard(
|
|
|
1122
1153
|
/**
|
|
1123
1154
|
* Build the needs-attention REASON for a task-SET ACCEPTANCE GATE block (task
|
|
1124
1155
|
* `slice-acceptance-gate`): the fresh-context review of the produced set returned
|
|
1125
|
-
* `block`, so the spec is
|
|
1126
|
-
*
|
|
1156
|
+
* `block`, so the spec is marked stuck on its per-item lock (post lock-cutover —
|
|
1157
|
+
* `state: stuck` with this reason on the lock entry, no `work/needs-attention/`
|
|
1158
|
+
* folder write) and NO tasks land. Takes the core's
|
|
1127
1159
|
* structured `reviewBlockReason` (the gate's blocking findings); falls back to a
|
|
1128
1160
|
* generic line when absent. DISTINCT from the improver loop's
|
|
1129
1161
|
* {@link decompositionUnclearReason} (which carries the loop's open questions).
|
|
@@ -1141,8 +1173,8 @@ function taskGateBlockedReason(
|
|
|
1141
1173
|
|
|
1142
1174
|
/**
|
|
1143
1175
|
* Build the needs-attention REASON for a decomposition-unclear loop verdict (the
|
|
1144
|
-
* spec is
|
|
1145
|
-
* tasks). Prose only — recorded as the spec's
|
|
1176
|
+
* spec is marked stuck on its per-item lock with these open questions, no guessed
|
|
1177
|
+
* tasks). Prose only — recorded as the spec's stuck-lock reason.
|
|
1146
1178
|
*/
|
|
1147
1179
|
function decompositionUnclearReason(slug: string, questions: string[]): string {
|
|
1148
1180
|
const head =
|
|
@@ -1427,6 +1459,218 @@ function collectEmittedTasks(
|
|
|
1427
1459
|
return out;
|
|
1428
1460
|
}
|
|
1429
1461
|
|
|
1462
|
+
/**
|
|
1463
|
+
* Compose the propose-mode PR BODY for a slice-set (task
|
|
1464
|
+
* `slicing-pr-body-summary-threading`): mirror the BUILD path's body threading
|
|
1465
|
+
* (`do.ts` — `body: agent.output`) with a summary drawn from what the tasker
|
|
1466
|
+
* ALREADY has in hand — the emitted task files. The shared
|
|
1467
|
+
* {@link composeProposeBody} then wraps this prose with its runner-owned header.
|
|
1468
|
+
*
|
|
1469
|
+
* Sections (all suppressed when empty, so the body stays honest):
|
|
1470
|
+
* 1. A lead line naming the source spec + emitted task count.
|
|
1471
|
+
* 2. **Tasks** — one bullet per task: `slug — title`, plus `covers:` (which
|
|
1472
|
+
* spec user stories it maps to), `blockedBy:`, and `needsAnswers` if set.
|
|
1473
|
+
* 3. **Dependency graph** — the KEYSTONE(S) (tasks whose `blockedBy` names no
|
|
1474
|
+
* other task in this set, so they land first) and the `blockedBy` edges.
|
|
1475
|
+
* 4. **Needs answers** — any open questions the tasker review→edit loop
|
|
1476
|
+
* appended under `## Open questions` on `needsAnswers: true` tasks, so the
|
|
1477
|
+
* deferred seams are visible on the PR (not buried in task bodies).
|
|
1478
|
+
*
|
|
1479
|
+
* Returns `undefined` for the empty set (no emitted tasks) so the provider's
|
|
1480
|
+
* `gh pr create --fill` fallback still applies on that edge (matches the build
|
|
1481
|
+
* path's absent-body behaviour).
|
|
1482
|
+
*/
|
|
1483
|
+
export function composeTaskingProposeBody(
|
|
1484
|
+
specSlug: string,
|
|
1485
|
+
emitTasks: Record<string, string>,
|
|
1486
|
+
): string | undefined {
|
|
1487
|
+
const entries = Object.entries(emitTasks);
|
|
1488
|
+
if (entries.length === 0) {
|
|
1489
|
+
return undefined;
|
|
1490
|
+
}
|
|
1491
|
+
const tasks = entries
|
|
1492
|
+
.map(([rel, content]) => parseTaskSummary(rel, content))
|
|
1493
|
+
.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
1494
|
+
const setSlugs = new Set(tasks.map((t) => t.slug));
|
|
1495
|
+
const lines: string[] = [];
|
|
1496
|
+
const n = tasks.length;
|
|
1497
|
+
lines.push(
|
|
1498
|
+
`Auto-tasked \`${specSlug}\` into ${n} backlog task${n === 1 ? '' : 's'}.`,
|
|
1499
|
+
);
|
|
1500
|
+
lines.push('');
|
|
1501
|
+
lines.push('## Tasks');
|
|
1502
|
+
lines.push('');
|
|
1503
|
+
for (const t of tasks) {
|
|
1504
|
+
lines.push(`- **${t.slug}** — ${t.title}`);
|
|
1505
|
+
if (t.covers.length > 0) {
|
|
1506
|
+
lines.push(` - covers: ${t.covers.map((c) => `US #${c}`).join(', ')}`);
|
|
1507
|
+
}
|
|
1508
|
+
if (t.blockedBy.length > 0) {
|
|
1509
|
+
lines.push(` - blockedBy: ${t.blockedBy.join(', ')}`);
|
|
1510
|
+
}
|
|
1511
|
+
if (t.needsAnswers) {
|
|
1512
|
+
lines.push(' - needsAnswers: true');
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
// Dependency graph: keystone(s) are tasks whose blockedBy names no OTHER
|
|
1516
|
+
// member of this set (an external blocker — e.g. a task already in `done/` —
|
|
1517
|
+
// does not prevent this set-relative starting point); the edges list every
|
|
1518
|
+
// set-internal `blockedBy` link so a reviewer can trace the ordering.
|
|
1519
|
+
const keystones = tasks.filter((t) =>
|
|
1520
|
+
t.blockedBy.every((b) => !setSlugs.has(b)),
|
|
1521
|
+
);
|
|
1522
|
+
const edges = tasks.flatMap((t) =>
|
|
1523
|
+
t.blockedBy.filter((b) => setSlugs.has(b)).map((b) => `${t.slug} ← ${b}`),
|
|
1524
|
+
);
|
|
1525
|
+
if (keystones.length > 0 || edges.length > 0) {
|
|
1526
|
+
lines.push('');
|
|
1527
|
+
lines.push('## Dependency graph');
|
|
1528
|
+
lines.push('');
|
|
1529
|
+
if (keystones.length > 0) {
|
|
1530
|
+
const word = keystones.length === 1 ? 'Keystone' : 'Keystones';
|
|
1531
|
+
lines.push(`- ${word}: ${keystones.map((k) => k.slug).join(', ')}`);
|
|
1532
|
+
}
|
|
1533
|
+
for (const edge of edges) {
|
|
1534
|
+
lines.push(`- ${edge}`);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
const uncertain = tasks.filter((t) => t.needsAnswers);
|
|
1538
|
+
if (uncertain.length > 0) {
|
|
1539
|
+
lines.push('');
|
|
1540
|
+
lines.push('## Needs answers');
|
|
1541
|
+
lines.push('');
|
|
1542
|
+
for (const t of uncertain) {
|
|
1543
|
+
lines.push(`- **${t.slug}**:`);
|
|
1544
|
+
if (t.openQuestions.length === 0) {
|
|
1545
|
+
lines.push(' - (no specific questions surfaced)');
|
|
1546
|
+
continue;
|
|
1547
|
+
}
|
|
1548
|
+
for (const q of t.openQuestions) {
|
|
1549
|
+
lines.push(` - ${q}`);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
return lines.join('\n');
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
/** Summary of one emitted task, for {@link composeTaskingProposeBody}. */
|
|
1557
|
+
interface TaskSummary {
|
|
1558
|
+
slug: string;
|
|
1559
|
+
title: string;
|
|
1560
|
+
covers: string[];
|
|
1561
|
+
blockedBy: string[];
|
|
1562
|
+
needsAnswers: boolean;
|
|
1563
|
+
openQuestions: string[];
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
/**
|
|
1567
|
+
* Parse one emitted task file into a {@link TaskSummary}. Uses
|
|
1568
|
+
* {@link parseFrontmatter} for the shared fields; scans the raw frontmatter
|
|
1569
|
+
* block for `covers:` (not currently modelled on the parser's typed shape) and
|
|
1570
|
+
* the body for a trailing `## Open questions` bullet list (the loop's
|
|
1571
|
+
* uncertain-task disposition sink, see {@link appendQuestionsBlock}).
|
|
1572
|
+
*/
|
|
1573
|
+
function parseTaskSummary(rel: string, content: string): TaskSummary {
|
|
1574
|
+
const fm = parseFrontmatter(content);
|
|
1575
|
+
const slug = fm.slug ?? basename(rel).replace(/\.md$/i, '');
|
|
1576
|
+
const title = readTitleField(content) ?? slug;
|
|
1577
|
+
return {
|
|
1578
|
+
slug,
|
|
1579
|
+
title,
|
|
1580
|
+
covers: readCoversField(content),
|
|
1581
|
+
blockedBy: fm.blockedBy,
|
|
1582
|
+
needsAnswers: fm.needsAnswers === true,
|
|
1583
|
+
openQuestions: readOpenQuestions(content),
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/**
|
|
1588
|
+
* Extract the `title:` scalar from the raw frontmatter block (not on
|
|
1589
|
+
* {@link parseFrontmatter}'s typed shape). Missing / malformed ⇒ `undefined`.
|
|
1590
|
+
* Comment-tolerant: a trailing `# ...` after an UNQUOTED value is stripped.
|
|
1591
|
+
*/
|
|
1592
|
+
function readTitleField(content: string): string | undefined {
|
|
1593
|
+
const normalized = content.replace(/\r\n/g, '\n').replace(/^\uFEFF/, '');
|
|
1594
|
+
if (!normalized.startsWith('---\n')) {
|
|
1595
|
+
return undefined;
|
|
1596
|
+
}
|
|
1597
|
+
const lines = normalized.split('\n');
|
|
1598
|
+
const closing = lines.indexOf('---', 1);
|
|
1599
|
+
if (closing === -1) {
|
|
1600
|
+
return undefined;
|
|
1601
|
+
}
|
|
1602
|
+
for (let i = 1; i < closing; i++) {
|
|
1603
|
+
const m = lines[i].match(/^title:\s*(.*)$/);
|
|
1604
|
+
if (!m) continue;
|
|
1605
|
+
let value = m[1].trim();
|
|
1606
|
+
if (value === '') return undefined;
|
|
1607
|
+
// Quoted ⇒ return inner text verbatim; unquoted ⇒ strip trailing `# ...`.
|
|
1608
|
+
if (
|
|
1609
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
1610
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
1611
|
+
) {
|
|
1612
|
+
return value.slice(1, -1);
|
|
1613
|
+
}
|
|
1614
|
+
const hash = value.indexOf('#');
|
|
1615
|
+
if (hash !== -1) value = value.slice(0, hash).trimEnd();
|
|
1616
|
+
return value === '' ? undefined : value;
|
|
1617
|
+
}
|
|
1618
|
+
return undefined;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
/**
|
|
1622
|
+
* Extract the `covers: [...]` inline-list values from the raw frontmatter block
|
|
1623
|
+
* of a task (not on {@link parseFrontmatter}'s typed shape, so we scan the raw
|
|
1624
|
+
* fence here). Missing / malformed / empty ⇒ `[]`. Comment-tolerant (a trailing
|
|
1625
|
+
* `# ...` after the `]` is ignored, matching the house style on `blockedBy: []
|
|
1626
|
+
* # startable now`).
|
|
1627
|
+
*/
|
|
1628
|
+
function readCoversField(content: string): string[] {
|
|
1629
|
+
const normalized = content.replace(/\r\n/g, '\n').replace(/^\uFEFF/, '');
|
|
1630
|
+
if (!normalized.startsWith('---\n')) {
|
|
1631
|
+
return [];
|
|
1632
|
+
}
|
|
1633
|
+
const lines = normalized.split('\n');
|
|
1634
|
+
const closing = lines.indexOf('---', 1);
|
|
1635
|
+
if (closing === -1) {
|
|
1636
|
+
return [];
|
|
1637
|
+
}
|
|
1638
|
+
for (let i = 1; i < closing; i++) {
|
|
1639
|
+
const line = lines[i];
|
|
1640
|
+
const m = line.match(/^covers:\s*\[([^\]]*)\]/);
|
|
1641
|
+
if (!m) continue;
|
|
1642
|
+
return m[1]
|
|
1643
|
+
.split(',')
|
|
1644
|
+
.map((s) => s.trim().replace(/^['"]|['"]$/g, ''))
|
|
1645
|
+
.filter((s) => s !== '');
|
|
1646
|
+
}
|
|
1647
|
+
return [];
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
/**
|
|
1651
|
+
* Extract the bullets under a trailing `## Open questions` heading in a task
|
|
1652
|
+
* body — the tasker review loop's uncertain-task sink (see
|
|
1653
|
+
* {@link appendQuestionsBlock}). Returns `[]` when no such block exists.
|
|
1654
|
+
*/
|
|
1655
|
+
function readOpenQuestions(content: string): string[] {
|
|
1656
|
+
const normalized = content.replace(/\r\n/g, '\n');
|
|
1657
|
+
const idx = normalized.lastIndexOf(`\n${OPEN_QUESTIONS_HEADING}`);
|
|
1658
|
+
if (idx === -1) {
|
|
1659
|
+
return [];
|
|
1660
|
+
}
|
|
1661
|
+
const tail = normalized.slice(idx + 1);
|
|
1662
|
+
const lines = tail.split('\n').slice(1);
|
|
1663
|
+
const questions: string[] = [];
|
|
1664
|
+
for (const raw of lines) {
|
|
1665
|
+
const line = raw.trimEnd();
|
|
1666
|
+
if (line === '') continue;
|
|
1667
|
+
const m = line.match(/^- (.*)$/);
|
|
1668
|
+
if (!m) break;
|
|
1669
|
+
questions.push(m[1]);
|
|
1670
|
+
}
|
|
1671
|
+
return questions;
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1430
1674
|
/** List `*.md` files in `dir`, sorted; an absent dir reads as empty. */
|
|
1431
1675
|
function listMarkdown(dir: string): string[] {
|
|
1432
1676
|
let entries: string[];
|
package/src/triage-persist.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import {resolveSidecarIdentity, sidecarPathFor} from './sidecar.js';
|
|
11
11
|
import type {TriageAutoKind} from './triage-gate.js';
|
|
12
12
|
import {renderTaskBody, renderSpecBody} from './buildable-body.js';
|
|
13
|
+
import {setFrontmatterMarker} from './frontmatter.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* The engine-owned TRIAGE PERSIST (spec `advance-loop`, task `advance-rung-triage`,
|
|
@@ -233,7 +234,7 @@ export interface PromoteObservationOptions {
|
|
|
233
234
|
* CAS) — NOT intake's branch+integrate band — so the CAS-loser-backs-off
|
|
234
235
|
* guarantee and the same-commit note deletion are uniform across the two routes.
|
|
235
236
|
* MIGRATE step (spec `prd-to-spec-vocabulary-cutover-and-migration-command`): the
|
|
236
|
-
* produced VALUE is `'spec'` (the verdict outcome renamed from
|
|
237
|
+
* produced VALUE is `'spec'` (the verdict outcome renamed from ''prd'').
|
|
237
238
|
*/
|
|
238
239
|
artifact?: 'task' | 'spec';
|
|
239
240
|
/**
|
|
@@ -273,11 +274,20 @@ export interface PromoteObservationOptions {
|
|
|
273
274
|
export interface PromoteObservationResult {
|
|
274
275
|
/**
|
|
275
276
|
* `promoted` (the new item landed via the CAS + the observation+sidecar were
|
|
276
|
-
* deleted in the SAME commit), `
|
|
277
|
-
* the
|
|
278
|
-
*
|
|
277
|
+
* deleted in the SAME commit), `already-triaged` (the target task ALREADY exists
|
|
278
|
+
* on the arbiter AND was PROVABLY minted from THIS observation in a prior run — a
|
|
279
|
+
* TERMINAL, benign idempotency fact, exit 0; the observation is left intact but a
|
|
280
|
+
* retry can never succeed, so it is NOT loud), `lost` (a genuine concurrent-create
|
|
281
|
+
* race for a same-path NEW item was lost — the loser backs off, observation left
|
|
282
|
+
* INTACT, retry helps), or `contended` (the CAS push kept failing). Maps onto the
|
|
283
|
+
* claim-CAS exit codes.
|
|
279
284
|
*/
|
|
280
|
-
outcome:
|
|
285
|
+
outcome:
|
|
286
|
+
| 'promoted'
|
|
287
|
+
| 'already-triaged'
|
|
288
|
+
| 'lost'
|
|
289
|
+
| 'contended'
|
|
290
|
+
| 'usage-error';
|
|
281
291
|
exitCode: 0 | 1 | 2 | 3;
|
|
282
292
|
/** The new task's path (relative to repo root) on a WIN. */
|
|
283
293
|
newItemPath?: string;
|
|
@@ -293,7 +303,7 @@ export interface PromoteObservationResult {
|
|
|
293
303
|
* (create + delete).
|
|
294
304
|
*
|
|
295
305
|
* The artifact TYPE (`options.artifact`) selects the target + body shape: `'task'`
|
|
296
|
-
* (default) mints `work/tasks/ready/<slug>.md`; `'
|
|
306
|
+
* (default) mints `work/tasks/ready/<slug>.md`; `'spec'` mints
|
|
297
307
|
* `work/specs/proposed/<slug>.md` (SPEC staging — a human later promotes it to
|
|
298
308
|
* `ready/`). BOTH routes use the SAME triage-local {@link createItemThroughCas}
|
|
299
309
|
* writer (NOT intake's `switchToWorkBranch`/`performIntegration` branch+integrate
|
|
@@ -340,11 +350,24 @@ export async function promoteObservation(
|
|
|
340
350
|
`${newSlug}.md`,
|
|
341
351
|
);
|
|
342
352
|
const by = options.by || resolveBy(cwd, env);
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
353
|
+
// Stamp the observation→task PROVENANCE back-reference `promotedFrom: <item>` into
|
|
354
|
+
// the minted body (task `observation-triage-already-triaged-benign-skip`). It is
|
|
355
|
+
// the PROVABLE LINK the create-CAS lost-race disambiguator reads to tell case A
|
|
356
|
+
// (this observation ALREADY minted this task — already triaged, benign skip) from
|
|
357
|
+
// case B (a genuine concurrent-create race with an UNRELATED same-path item — loud
|
|
358
|
+
// exit 2). Distinct from `origin:` (how BORN: human/issue) and `reviewOf:` (the
|
|
359
|
+
// review-nits back-pointer); this is the triage-promote lineage. Matching on this
|
|
360
|
+
// explicit field — not the slug — is what keeps an unrelated same-slug task from
|
|
361
|
+
// false-positiving into a benign skip.
|
|
362
|
+
const content = setFrontmatterMarker(
|
|
363
|
+
ensureTaskDispatchable(
|
|
364
|
+
artifact,
|
|
365
|
+
newSlug,
|
|
366
|
+
options.stubContent ??
|
|
367
|
+
buildPromotedBody(artifact, newSlug, readItem(cwd, itemPath, env)),
|
|
368
|
+
),
|
|
369
|
+
'promotedFrom',
|
|
370
|
+
item,
|
|
348
371
|
);
|
|
349
372
|
|
|
350
373
|
// The note + its answered sidecar `git rm` IN THE SAME create commit (promote =
|
|
@@ -359,6 +382,10 @@ export async function promoteObservation(
|
|
|
359
382
|
path: newItemPath,
|
|
360
383
|
content,
|
|
361
384
|
deletePaths: [itemPath, sidecarPath],
|
|
385
|
+
// The provenance disambiguator: on a LOST create-race, the CAS reads the
|
|
386
|
+
// existing file's `promotedFrom:` and, iff it equals THIS observation, reports
|
|
387
|
+
// the benign `already-exists-from-source` instead of the loud `lost`.
|
|
388
|
+
sourceItem: item,
|
|
362
389
|
cwd,
|
|
363
390
|
arbiter: options.arbiter,
|
|
364
391
|
by,
|
|
@@ -369,6 +396,23 @@ export async function promoteObservation(
|
|
|
369
396
|
? {contention: options.contention}
|
|
370
397
|
: {}),
|
|
371
398
|
});
|
|
399
|
+
// TERMINAL 'already triaged' (case A): the task this observation would mint
|
|
400
|
+
// ALREADY exists on the arbiter AND was provably minted from THIS observation in
|
|
401
|
+
// a prior run. A benign idempotency fact (exit 0) — the human already decided when
|
|
402
|
+
// they minted it — NOT a retry. The observation is left intact (a later
|
|
403
|
+
// discharge-by-deletion or a human deletes it); this leg does not red CI.
|
|
404
|
+
if (created.outcome === 'already-exists-from-source') {
|
|
405
|
+
const message =
|
|
406
|
+
`promote ${item}: ${newItemPath} was already minted FROM this observation ` +
|
|
407
|
+
`in a prior run (${created.message}) — already triaged. Benign skip, no retry.`;
|
|
408
|
+
note(message);
|
|
409
|
+
return {
|
|
410
|
+
outcome: 'already-triaged',
|
|
411
|
+
exitCode: 0,
|
|
412
|
+
newItemPath,
|
|
413
|
+
message,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
372
416
|
if (created.exitCode !== 0) {
|
|
373
417
|
const message =
|
|
374
418
|
`promote ${item}: the new item ${newItemPath} ${created.outcome} the ` +
|
|
@@ -398,7 +442,7 @@ export async function promoteObservation(
|
|
|
398
442
|
*
|
|
399
443
|
* The artifact TYPE selects the frontmatter + lead heading: a `task` gets
|
|
400
444
|
* `## What to build` + `blockedBy: []` + a `## Prompt` (the buildable-task shape);
|
|
401
|
-
* a `
|
|
445
|
+
* a `spec` gets `## Problem Statement` (the SPEC document shape, with no `blockedBy` —
|
|
402
446
|
* a SPEC is not a blockable task — and no `## Prompt`, since a SPEC is not
|
|
403
447
|
* dispatched by `do`/`run`). BOTH carry the SAME transcribed mechanism prose +
|
|
404
448
|
* open-question block, so a SPEC minted into `proposed/` is just as self-contained
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-present weth, LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Vendored: `incur/src/internal/agents.ts` (MIT)
|
|
2
|
+
|
|
3
|
+
`agents.ts` here is a **verbatim** copy of `src/internal/agents.ts` from
|
|
4
|
+
[wevm/incur](https://github.com/wevm/incur) (MIT). See `LICENSE` beside it for the
|
|
5
|
+
required copyright + permission notice, which is also duplicated as a header
|
|
6
|
+
comment at the top of `agents.ts` itself so the file stays attribution-complete
|
|
7
|
+
in isolation.
|
|
8
|
+
|
|
9
|
+
We VENDOR rather than depend (see
|
|
10
|
+
`docs/adr/skill-install-vendors-incur-agents-map.md`): the harness-destination
|
|
11
|
+
map + copy/symlink logic are the one hard, drift-prone piece we need, and the
|
|
12
|
+
file is dependency-free (only `node:fs`/`os`/`path`). Keep it BYTE-CLOSE to
|
|
13
|
+
upstream so a future incur update is a mechanical re-copy — put all wrapper
|
|
14
|
+
code outside this directory (`../../install-skills.ts`).
|
|
15
|
+
|
|
16
|
+
To refresh: re-fetch `src/internal/agents.ts` from the upstream tag you want,
|
|
17
|
+
overwrite `agents.ts`, keep the small ATTRIBUTION HEADER block at the top
|
|
18
|
+
(above the original `import` lines) intact, and re-copy `LICENSE` if it
|
|
19
|
+
changed upstream.
|