dorfl 0.1.0 → 0.1.2
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-ci-template.d.ts +3 -3
- package/dist/advance-ci-template.js +1 -1
- package/dist/advance-ci-template.js.map +1 -1
- package/dist/advance-classify.d.ts +5 -5
- package/dist/advance-classify.d.ts.map +1 -1
- package/dist/advance-classify.js +4 -4
- package/dist/advance-drivers.d.ts +7 -7
- package/dist/advance-drivers.d.ts.map +1 -1
- package/dist/advance-drivers.js +5 -5
- package/dist/advance-drivers.js.map +1 -1
- package/dist/advance-isolated.d.ts +3 -3
- package/dist/advance-isolated.d.ts.map +1 -1
- package/dist/advance-isolated.js +1 -1
- package/dist/advance-lifecycle-template.d.ts +2 -2
- package/dist/advance-lifecycle-template.js +9 -9
- package/dist/advance-lifecycle-template.js.map +1 -1
- package/dist/advance-loop-driver.d.ts +3 -3
- package/dist/advance-loop-driver.d.ts.map +1 -1
- package/dist/advance-loop-driver.js +1 -1
- package/dist/advance-treeless-publish.d.ts +1 -1
- package/dist/advance.d.ts +11 -11
- package/dist/advance.d.ts.map +1 -1
- package/dist/advance.js +21 -21
- package/dist/advance.js.map +1 -1
- package/dist/advancing-lock.d.ts +1 -1
- package/dist/advancing-lock.js +5 -5
- package/dist/apply-decide.d.ts +2 -2
- package/dist/apply-decide.js +2 -2
- package/dist/apply-merge-action.d.ts +4 -4
- package/dist/apply-merge-action.js +2 -2
- package/dist/apply-persist.d.ts +5 -5
- package/dist/apply-persist.d.ts.map +1 -1
- package/dist/apply-persist.js +7 -7
- package/dist/apply-persist.js.map +1 -1
- package/dist/buildable-body.d.ts +14 -14
- package/dist/buildable-body.js +9 -9
- package/dist/claim-cas.d.ts +3 -3
- package/dist/claim-cas.js +1 -1
- package/dist/claim-cas.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +83 -77
- package/dist/cli.js.map +1 -1
- package/dist/close-job-template.d.ts +3 -3
- package/dist/close-job-template.js +8 -8
- package/dist/close-job.d.ts +8 -8
- package/dist/close-job.js +25 -25
- package/dist/close-job.js.map +1 -1
- package/dist/complete.d.ts +3 -3
- package/dist/complete.js +8 -8
- package/dist/complete.js.map +1 -1
- package/dist/concurrency.d.ts +1 -1
- package/dist/concurrency.js +1 -1
- package/dist/config.d.ts +32 -32
- package/dist/config.js +5 -5
- package/dist/config.js.map +1 -1
- package/dist/continue-branch.d.ts +1 -1
- package/dist/continue-branch.js +1 -1
- package/dist/cwd-section.js +1 -1
- package/dist/cwd-section.js.map +1 -1
- package/dist/decision-engine.d.ts +2 -2
- package/dist/do-autopick.d.ts +6 -6
- package/dist/do-autopick.d.ts.map +1 -1
- package/dist/do-autopick.js +5 -5
- package/dist/do-autopick.js.map +1 -1
- package/dist/do-config.d.ts +3 -3
- package/dist/do-config.js +3 -3
- package/dist/do-config.js.map +1 -1
- package/dist/do-remote-auto.d.ts +2 -2
- package/dist/do-remote-auto.js +1 -1
- package/dist/do.d.ts +25 -25
- package/dist/do.d.ts.map +1 -1
- package/dist/do.js +22 -22
- package/dist/do.js.map +1 -1
- package/dist/drop-source.d.ts +2 -2
- package/dist/env-config.js +8 -8
- package/dist/env-config.js.map +1 -1
- package/dist/format.js +3 -3
- package/dist/format.js.map +1 -1
- package/dist/frontmatter.d.ts +33 -39
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js +18 -31
- package/dist/frontmatter.js.map +1 -1
- package/dist/install-ci-branch-protection.d.ts +1 -1
- package/dist/install-ci-branch-protection.js +1 -1
- package/dist/install-ci-capabilities/advance-lifecycle.d.ts +1 -1
- package/dist/install-ci-capabilities/advance-lifecycle.js +1 -1
- package/dist/install-ci-capabilities/close-job.d.ts +1 -1
- package/dist/install-ci-capabilities/close-job.js +1 -1
- package/dist/install-ci-capabilities/example-noop.d.ts +1 -1
- package/dist/install-ci-capabilities/example-noop.js +1 -1
- package/dist/install-ci-capabilities/intake.d.ts +2 -2
- package/dist/install-ci-capabilities/intake.js +2 -2
- package/dist/install-ci-capabilities/verify.d.ts +1 -1
- package/dist/install-ci-capabilities/verify.js +1 -1
- package/dist/install-ci-core.d.ts +3 -3
- package/dist/install-ci-core.js +2 -2
- package/dist/install-ci-core.js.map +1 -1
- package/dist/install-ci-github.d.ts +1 -1
- package/dist/install-ci-github.js +1 -1
- package/dist/install-ci.d.ts +1 -1
- package/dist/install-ci.js +4 -4
- package/dist/install-ci.js.map +1 -1
- package/dist/intake-event.d.ts +6 -6
- package/dist/intake-event.js +6 -6
- package/dist/intake-marker.d.ts +3 -3
- package/dist/intake-marker.d.ts.map +1 -1
- package/dist/intake-triage.d.ts +1 -1
- package/dist/intake-triage.js +2 -2
- package/dist/intake-trigger-template.d.ts +8 -8
- package/dist/intake-trigger-template.d.ts.map +1 -1
- package/dist/intake-trigger-template.js +16 -16
- package/dist/intake-trigger-template.js.map +1 -1
- package/dist/intake.d.ts +50 -50
- package/dist/intake.d.ts.map +1 -1
- package/dist/intake.js +70 -70
- package/dist/intake.js.map +1 -1
- package/dist/integration-core.d.ts +17 -17
- package/dist/integration-core.js +17 -17
- package/dist/integration-core.js.map +1 -1
- package/dist/isolation.d.ts +3 -3
- package/dist/issue-provider.d.ts +4 -4
- package/dist/issue-provider.js +1 -1
- package/dist/item-lock.d.ts +21 -21
- package/dist/item-lock.d.ts.map +1 -1
- package/dist/item-lock.js +16 -16
- package/dist/item-path.d.ts +2 -2
- package/dist/item-path.js +2 -2
- package/dist/ledger-lint.d.ts +9 -9
- package/dist/ledger-lint.js +9 -9
- package/dist/ledger-read.d.ts +57 -57
- package/dist/ledger-read.d.ts.map +1 -1
- package/dist/ledger-read.js +24 -24
- package/dist/ledger-read.js.map +1 -1
- package/dist/ledger-write.d.ts +2 -2
- package/dist/ledger-write.js +1 -1
- package/dist/lifecycle-gather.d.ts +1 -1
- package/dist/lifecycle-gather.js +16 -16
- package/dist/lifecycle-gather.js.map +1 -1
- package/dist/lifecycle-pools.d.ts +4 -4
- package/dist/lifecycle-pools.js +1 -1
- package/dist/merge-question-surfacer.d.ts +3 -3
- package/dist/merge-question-surfacer.js +2 -2
- package/dist/merge-question-surfacer.js.map +1 -1
- package/dist/mint-adr.js +4 -4
- package/dist/mirror-pool-scan.d.ts +2 -2
- package/dist/mirror-pool-scan.js +3 -3
- package/dist/mirror-pool-scan.js.map +1 -1
- package/dist/needs-attention.d.ts +13 -13
- package/dist/needs-attention.d.ts.map +1 -1
- package/dist/needs-attention.js +11 -11
- package/dist/needs-attention.js.map +1 -1
- package/dist/orphan-sidecar.d.ts +2 -2
- package/dist/placement.d.ts +6 -6
- package/dist/placement.js +2 -2
- package/dist/prd-to-spec.d.ts +15 -1
- package/dist/prd-to-spec.d.ts.map +1 -1
- package/dist/prd-to-spec.js +36 -14
- package/dist/prd-to-spec.js.map +1 -1
- package/dist/prompt.d.ts +24 -24
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +29 -31
- package/dist/prompt.js.map +1 -1
- package/dist/protocol/CLAIM-PROTOCOL.md +4 -4
- package/dist/protocol/REVIEW-PROTOCOL.md +1 -1
- package/dist/protocol/TASKING-PROTOCOL.md +12 -12
- package/dist/protocol/WORK-CONTRACT.md +39 -39
- package/dist/protocol/spec-template.md +1 -1
- package/dist/protocol/task-template.md +3 -3
- package/dist/readiness.d.ts +1 -1
- package/dist/recover-isolated.js +1 -1
- package/dist/repo-config.js +12 -12
- package/dist/repo-config.js.map +1 -1
- package/dist/review-gate.d.ts +3 -3
- package/dist/review-gate.d.ts.map +1 -1
- package/dist/review-gate.js +11 -10
- package/dist/review-gate.js.map +1 -1
- package/dist/review-verdict.d.ts +2 -2
- package/dist/review-verdict.d.ts.map +1 -1
- package/dist/review-verdict.js +1 -1
- package/dist/run.d.ts +1 -1
- package/dist/run.js +5 -5
- package/dist/run.js.map +1 -1
- package/dist/scan.d.ts +10 -10
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +8 -8
- package/dist/scan.js.map +1 -1
- package/dist/select-order.d.ts +1 -1
- package/dist/select-priority.d.ts +12 -12
- package/dist/select-priority.d.ts.map +1 -1
- package/dist/select-priority.js +5 -5
- package/dist/select-priority.js.map +1 -1
- package/dist/sidecar.d.ts +2 -2
- package/dist/sidecar.js +2 -2
- package/dist/sidecar.js.map +1 -1
- package/dist/slug-namespace.d.ts +1 -1
- package/dist/slug-namespace.js +3 -3
- package/dist/slug-namespace.js.map +1 -1
- package/dist/spec-complete.js +2 -2
- package/dist/spec-complete.js.map +1 -1
- package/dist/start.d.ts +1 -1
- package/dist/start.js +3 -3
- package/dist/start.js.map +1 -1
- package/dist/status.d.ts +3 -3
- package/dist/status.js +4 -4
- package/dist/status.js.map +1 -1
- package/dist/surface-gate.d.ts +2 -2
- package/dist/surface-gate.d.ts.map +1 -1
- package/dist/surface-persist.d.ts +2 -2
- package/dist/surface-persist.d.ts.map +1 -1
- package/dist/tasker-review-loop.d.ts +8 -8
- package/dist/tasker-review-loop.d.ts.map +1 -1
- package/dist/tasker-review-loop.js +8 -8
- package/dist/tasker-review-loop.js.map +1 -1
- package/dist/tasking-eligibility.d.ts +21 -21
- package/dist/tasking-eligibility.d.ts.map +1 -1
- package/dist/tasking-eligibility.js +10 -10
- package/dist/tasking-lock.d.ts +8 -8
- package/dist/tasking-lock.d.ts.map +1 -1
- package/dist/tasking-lock.js +20 -21
- package/dist/tasking-lock.js.map +1 -1
- package/dist/tasking.d.ts +33 -33
- package/dist/tasking.d.ts.map +1 -1
- package/dist/tasking.js +89 -88
- package/dist/tasking.js.map +1 -1
- package/dist/triage-gate.d.ts +1 -1
- package/dist/triage-persist.d.ts +5 -5
- package/dist/triage-persist.js +15 -15
- package/dist/triage-persist.js.map +1 -1
- package/dist/verify-workflow-template.d.ts +1 -1
- package/dist/verify-workflow-template.js +2 -2
- package/dist/work-layout.d.ts +9 -7
- package/dist/work-layout.d.ts.map +1 -1
- package/dist/work-layout.js +9 -7
- package/dist/work-layout.js.map +1 -1
- package/dist/workspace.d.ts +1 -1
- package/package.json +1 -1
- package/src/advance-ci-template.ts +4 -4
- package/src/advance-classify.ts +5 -5
- package/src/advance-drivers.ts +10 -10
- package/src/advance-isolated.ts +3 -3
- package/src/advance-lifecycle-template.ts +9 -9
- package/src/advance-loop-driver.ts +3 -3
- package/src/advance-treeless-publish.ts +1 -1
- package/src/advance.ts +30 -30
- package/src/advancing-lock.ts +6 -6
- package/src/apply-decide.ts +2 -2
- package/src/apply-merge-action.ts +5 -5
- package/src/apply-persist.ts +8 -8
- package/src/buildable-body.ts +14 -14
- package/src/claim-cas.ts +4 -4
- package/src/cli.ts +94 -84
- package/src/close-job-template.ts +8 -8
- package/src/close-job.ts +26 -26
- package/src/complete.ts +11 -11
- package/src/concurrency.ts +1 -1
- package/src/config.ts +37 -37
- package/src/continue-branch.ts +1 -1
- package/src/cwd-section.ts +1 -1
- package/src/decision-engine.ts +3 -3
- package/src/do-autopick.ts +9 -9
- package/src/do-config.ts +6 -6
- package/src/do-remote-auto.ts +2 -2
- package/src/do.ts +49 -49
- package/src/drop-source.ts +2 -2
- package/src/env-config.ts +8 -8
- package/src/format.ts +3 -3
- package/src/frontmatter.ts +42 -60
- package/src/install-ci-branch-protection.ts +1 -1
- package/src/install-ci-capabilities/advance-lifecycle.ts +1 -1
- package/src/install-ci-capabilities/close-job.ts +1 -1
- package/src/install-ci-capabilities/example-noop.ts +1 -1
- package/src/install-ci-capabilities/intake.ts +2 -2
- package/src/install-ci-capabilities/verify.ts +1 -1
- package/src/install-ci-core.ts +4 -4
- package/src/install-ci-github.ts +1 -1
- package/src/install-ci.ts +4 -4
- package/src/intake-event.ts +6 -6
- package/src/intake-marker.ts +3 -3
- package/src/intake-triage.ts +2 -2
- package/src/intake-trigger-template.ts +17 -17
- package/src/intake.ts +97 -97
- package/src/integration-core.ts +35 -35
- package/src/isolation.ts +3 -3
- package/src/issue-provider.ts +4 -4
- package/src/item-lock.ts +21 -21
- package/src/item-path.ts +2 -2
- package/src/ledger-lint.ts +9 -9
- package/src/ledger-read.ts +81 -81
- package/src/ledger-write.ts +3 -3
- package/src/lifecycle-gather.ts +16 -16
- package/src/lifecycle-pools.ts +4 -4
- package/src/merge-question-surfacer.ts +4 -4
- package/src/mint-adr.ts +4 -4
- package/src/mirror-pool-scan.ts +5 -5
- package/src/needs-attention.ts +22 -22
- package/src/orphan-sidecar.ts +2 -2
- package/src/placement.ts +6 -6
- package/src/prd-to-spec.ts +51 -15
- package/src/prompt.ts +33 -35
- package/src/readiness.ts +1 -1
- package/src/recover-isolated.ts +1 -1
- package/src/repo-config.ts +12 -12
- package/src/review-gate.ts +12 -11
- package/src/review-verdict.ts +2 -2
- package/src/run.ts +6 -6
- package/src/scan.ts +15 -15
- package/src/select-order.ts +1 -1
- package/src/select-priority.ts +13 -13
- package/src/sidecar.ts +3 -3
- package/src/slug-namespace.ts +3 -3
- package/src/spec-complete.ts +2 -2
- package/src/start.ts +4 -4
- package/src/status.ts +7 -7
- package/src/surface-gate.ts +2 -2
- package/src/surface-persist.ts +2 -2
- package/src/tasker-review-loop.ts +15 -15
- package/src/tasking-eligibility.ts +21 -21
- package/src/tasking-lock.ts +27 -28
- package/src/tasking.ts +124 -123
- package/src/triage-gate.ts +1 -1
- package/src/triage-persist.ts +16 -16
- package/src/verify-workflow-template.ts +2 -2
- package/src/work-layout.ts +9 -7
- package/src/workspace.ts +1 -1
package/src/tasking.ts
CHANGED
|
@@ -48,44 +48,44 @@ import {
|
|
|
48
48
|
import type {ReviewGate} from './review-gate.js';
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
* The **`do
|
|
51
|
+
* The **`do spec:<slug>` tasking path** (spec `auto-slice`, task
|
|
52
52
|
* `autoslice-command`) — the orchestration that ties the tasking GATE
|
|
53
53
|
* (`tasking-eligibility.ts`) and the tasking LOCK (`tasking-lock.ts`) together to
|
|
54
|
-
* task a
|
|
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
56
|
* moves them `pre-backlog/ → backlog/` later), with the RUNNER owning every git-state
|
|
57
|
-
* transition. This is the
|
|
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
|
-
* `do.ts` dispatches `resolved.namespace === '
|
|
59
|
+
* `do.ts` dispatches `resolved.namespace === 'spec'` here.
|
|
60
60
|
*
|
|
61
61
|
* The end-to-end flow (mirroring the `do`/`run` runner-owns-git discipline — the
|
|
62
62
|
* agent only EDITS files, the runner does ALL git):
|
|
63
63
|
*
|
|
64
|
-
* 1. **Resolve the gate** (agent path): refuse to task a
|
|
65
|
-
* `humanOnly`/`needsAnswers`, or whose `taskedAfter`
|
|
64
|
+
* 1. **Resolve the gate** (agent path): refuse to task a spec that is
|
|
65
|
+
* `humanOnly`/`needsAnswers`, or whose `taskedAfter` specs are not yet tasked.
|
|
66
66
|
* The repo's `autoTask` POLICY also refuses on the AUTO-PICK pool path, but
|
|
67
|
-
* NOT when the
|
|
67
|
+
* NOT when the spec was named EXPLICITLY (`do spec:<slug>`, `explicit: true`):
|
|
68
68
|
* naming it IS the authorization, exactly as `do <task>` builds regardless of
|
|
69
69
|
* `autoBuild` (the pool, not the explicit claim, gates the policy). The HUMAN
|
|
70
70
|
* path is unbound by the gate entirely.
|
|
71
71
|
* 2. **Acquire the lock** (agent path) via the unified per-item lock CAS —
|
|
72
|
-
* serialising concurrent taskers on the `
|
|
73
|
-
* `work/
|
|
72
|
+
* serialising concurrent taskers on the `spec:<slug>` ref (the body STAYS in
|
|
73
|
+
* `work/specs/ready/`; the lock no longer moves it). The HUMAN path with no contention
|
|
74
74
|
* may task on `main` directly WITHOUT the lock.
|
|
75
|
-
* 3. **Invoke the agent harness** with the `to-task`
|
|
75
|
+
* 3. **Invoke the agent harness** with the `to-task` spec — the agent runs the
|
|
76
76
|
* tasker methodology and produces `work/tasks/backlog/<slug>.md` FILES ONLY; it does
|
|
77
77
|
* NOT commit/push/move (the same in-band boundary as the build agent).
|
|
78
78
|
* 4. **The runner integrates the COMPLETING transition through the SHARED core**
|
|
79
79
|
* (`performIntegration`, task `slice-output-through-integration`): the agent's
|
|
80
80
|
* tasking runs on a `work/<slug>` branch cut from `<arbiter>/main` (whose base
|
|
81
|
-
* holds the
|
|
82
|
-
*
|
|
81
|
+
* holds the spec in `work/specs/ready/`), and the produced backlog tasks + the durable
|
|
82
|
+
* spec lifecycle move (`work/specs/ready/ → work/specs/tasked/`)
|
|
83
83
|
* integrate via the band honoring `--propose` (push the branch + open a
|
|
84
84
|
* PR, NO `main` touch) / `--merge` (land on `main`). Because the integrate-time
|
|
85
85
|
* args resolve ONCE in the shared core, EVERY `do task:` integrate arg applies
|
|
86
|
-
* to `do
|
|
86
|
+
* to `do spec:` by construction. A content-identity STALE CHECK (the lock's
|
|
87
87
|
* read-stability backstop) fires FIRST against the acquire-time `lockedBlob`,
|
|
88
|
-
* so a concurrent edit of the held
|
|
88
|
+
* so a concurrent edit of the held spec still fails loud (`stale`).
|
|
89
89
|
*
|
|
90
90
|
* The tasking LOCK (`tasking-lock.ts`, `acquireTaskingLock`/`releaseTaskingLock`)
|
|
91
91
|
* is the UNIFIED per-item lock (`refs/dorfl/lock/<entry>`, `action: task`)
|
|
@@ -98,20 +98,20 @@ import type {ReviewGate} from './review-gate.js';
|
|
|
98
98
|
* owned by `slicer-review-edit-loop`; this path produces + integrates the tasks.
|
|
99
99
|
*/
|
|
100
100
|
|
|
101
|
-
/** The terminal status of one `do
|
|
101
|
+
/** The terminal status of one `do spec:<slug>` tasking run. */
|
|
102
102
|
export type TaskOutcome =
|
|
103
103
|
| 'tasked' // gate passed (agent) / unbound (human) → lock → agent → committed
|
|
104
104
|
| 'gate-refused' // the agent gate refused (humanOnly/needsAnswers/autoTask/taskedAfter)
|
|
105
105
|
| 'lock-lost' // the lock was lost/contended (another tasker holds it)
|
|
106
106
|
| 'agent-failed' // the agent invocation itself errored
|
|
107
|
-
| 'stale' // the held
|
|
108
|
-
| 'needs-attention' // the tasker edit loop found the decomposition unclear →
|
|
109
|
-
| 'usage-error'; // usage / environment problem (missing
|
|
107
|
+
| 'stale' // the held spec was edited under the lock → the tasking is stale
|
|
108
|
+
| 'needs-attention' // the tasker edit loop found the decomposition unclear → spec routed to needs-attention (no tasks)
|
|
109
|
+
| 'usage-error'; // usage / environment problem (missing spec, bad release, …)
|
|
110
110
|
|
|
111
111
|
export interface TaskResult {
|
|
112
112
|
exitCode: 0 | 1 | 2 | 3 | 4;
|
|
113
113
|
outcome: TaskOutcome;
|
|
114
|
-
/** The
|
|
114
|
+
/** The spec slug acted on. */
|
|
115
115
|
slug: string;
|
|
116
116
|
/** Repo-relative paths of the backlog tasks the runner committed. */
|
|
117
117
|
emitted?: string[];
|
|
@@ -119,7 +119,7 @@ export interface TaskResult {
|
|
|
119
119
|
* The tasker review→edit LOOP's disposition (`slicer-review-edit-loop`), when
|
|
120
120
|
* the loop ran. `converged` = the improved tasks landed; `uncertain-tasks` =
|
|
121
121
|
* the cap was hit and specific tasks landed `needsAnswers: true`; absent when
|
|
122
|
-
* no loop ran or the
|
|
122
|
+
* no loop ran or the spec was routed to needs-attention (`outcome:
|
|
123
123
|
* 'needs-attention'`).
|
|
124
124
|
*/
|
|
125
125
|
loop?: 'converged' | 'uncertain-tasks';
|
|
@@ -128,8 +128,8 @@ export interface TaskResult {
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
/**
|
|
131
|
-
* The agent invocation: runs the `to-task`
|
|
132
|
-
* `work/tasks/backlog/<slug>.md` task files (and trimming the
|
|
131
|
+
* The agent invocation: runs the `to-task` spec in `cwd`, WRITING
|
|
132
|
+
* `work/tasks/backlog/<slug>.md` task files (and trimming the spec). It does NO git —
|
|
133
133
|
* the runner captures the produced files and commits them.
|
|
134
134
|
*/
|
|
135
135
|
export type TaskDorfl = (input: {
|
|
@@ -155,7 +155,7 @@ const DEFAULT_LOCK_SEAM: TaskingLockSeam = {
|
|
|
155
155
|
};
|
|
156
156
|
|
|
157
157
|
export interface PerformTaskOptions {
|
|
158
|
-
/** The
|
|
158
|
+
/** The spec slug to task (`work/specs/ready/<slug>.md`). */
|
|
159
159
|
slug: string;
|
|
160
160
|
/** The working clone/checkout the tasking runs in. */
|
|
161
161
|
cwd: string;
|
|
@@ -170,16 +170,16 @@ export interface PerformTaskOptions {
|
|
|
170
170
|
/** Per-repo `autoTask` policy (resolved by `autoslice-gate`). Agent path only. */
|
|
171
171
|
autoTask?: boolean;
|
|
172
172
|
/**
|
|
173
|
-
* The
|
|
174
|
-
* `autoTask` POLICY is already satisfied — naming the
|
|
173
|
+
* The spec was named EXPLICITLY by the operator (`do spec:<slug>`), so the
|
|
174
|
+
* `autoTask` POLICY is already satisfied — naming the spec IS the authorization,
|
|
175
175
|
* EXACTLY as `do <task>` builds a named task regardless of `autoBuild` (the
|
|
176
176
|
* build path's precedent: `autoBuild` gates the scan/selection POOL only, never
|
|
177
177
|
* `performDo`'s explicit claim). When `true`, the agent tasking gate drops the
|
|
178
|
-
* `autoTask` policy term and binds ONLY the
|
|
178
|
+
* `autoTask` policy term and binds ONLY the spec's own readiness axes
|
|
179
179
|
* (`humanOnly`/`needsAnswers`) + `taskedAfter`. Defaults `false`. Both the
|
|
180
|
-
* explicit `do
|
|
180
|
+
* explicit `do spec:` dispatch AND the auto-pick path pass `true` here: the
|
|
181
181
|
* auto-pick POOL (`do-autopick.ts`) is the single `autoTask`-enforcement point
|
|
182
|
-
* (a pool-ineligible
|
|
182
|
+
* (a pool-ineligible spec is never selected), so once a spec is dispatched its
|
|
183
183
|
* policy is already settled. Agent path only.
|
|
184
184
|
*/
|
|
185
185
|
explicit?: boolean;
|
|
@@ -201,7 +201,7 @@ export interface PerformTaskOptions {
|
|
|
201
201
|
* with (task `slice-output-through-integration`): `propose` (default — push the
|
|
202
202
|
* `work/<slug>` branch + open a PR carrying the tasks, NO `main` touch) or
|
|
203
203
|
* `merge` (land them on `main`). Resolved ONCE in {@link performIntegration},
|
|
204
|
-
* so EVERY `do task:` integrate-time arg applies to `do
|
|
204
|
+
* so EVERY `do task:` integrate-time arg applies to `do spec:` by construction.
|
|
205
205
|
* The AGENT path only; the human path commits its own output. Defaults to the
|
|
206
206
|
* system default (`propose`).
|
|
207
207
|
*/
|
|
@@ -226,7 +226,7 @@ export interface PerformTaskOptions {
|
|
|
226
226
|
* task-path mirror of the build Gate-2, riding {@link performIntegration}'s
|
|
227
227
|
* review-before-integrate block. When `review` resolves on, a FRESH-CONTEXT
|
|
228
228
|
* agent reviews the WHOLE produced task SET (coherence / dependency graph /
|
|
229
|
-
* gaps + overlap /
|
|
229
|
+
* gaps + overlap / spec-goal correct-if-implemented) BEFORE the tasks integrate;
|
|
230
230
|
* `approve` lands them, `block` routes the set to needs-attention. It is
|
|
231
231
|
* controlled by the BUILD `--review`/`--no-review`/`--review-model` family (ONE
|
|
232
232
|
* gate-configuration story shared with the build path) and is ONE-SHOT —
|
|
@@ -247,7 +247,7 @@ export interface PerformTaskOptions {
|
|
|
247
247
|
/** Injectable lock seam (tests stub acquire/release). Defaults to the real CAS. */
|
|
248
248
|
lock?: TaskingLockSeam;
|
|
249
249
|
/**
|
|
250
|
-
* **The per-repo TASK-PLACEMENT default** (
|
|
250
|
+
* **The per-repo TASK-PLACEMENT default** (spec
|
|
251
251
|
* `staging-pool-position-gate-and-trust-model` US #5, task
|
|
252
252
|
* `runner-deterministic-slice-placement-policy-and-precedence`). The
|
|
253
253
|
* resolved per-repo default landing for the tasker's emitted tasks, fed as
|
|
@@ -270,13 +270,13 @@ export interface PerformTaskOptions {
|
|
|
270
270
|
*/
|
|
271
271
|
explicitTasksLandIn?: 'pre-backlog' | 'ready';
|
|
272
272
|
/**
|
|
273
|
-
* **The tasker review→edit→converge LOOP** (`slicer-review-edit-loop`, GATES
|
|
274
|
-
* `work/
|
|
273
|
+
* **The tasker review→edit→converge LOOP** (`slicer-review-edit-loop`, GATES spec
|
|
274
|
+
* `work/specs/ready/review.md` RESOLVED DESIGN — Shape 2 / insertion point A). When
|
|
275
275
|
* provided, AFTER the agent produces candidate tasks (step 3) and BEFORE the
|
|
276
276
|
* runner finalises them (step 4), run the `review` SKILL as a review→edit→
|
|
277
277
|
* re-review loop that IMPROVES the candidate tasks in place, then routes the
|
|
278
278
|
* verdict through the three outcomes (converge→land / uncertain-task→
|
|
279
|
-
* needsAnswers / decomposition-unclear→
|
|
279
|
+
* needsAnswers / decomposition-unclear→spec-to-needs-attention). The seam is the
|
|
280
280
|
* review+edit gate (tests inject a canned verdict+edits; production:
|
|
281
281
|
* {@link harnessTaskReviewGate}). Omitted ⇒ NO loop (the candidate tasks land
|
|
282
282
|
* as-is — the pre-loop behaviour). The HUMAN path is unaffected (the loop runs
|
|
@@ -318,7 +318,7 @@ export interface PerformTaskOptions {
|
|
|
318
318
|
const DEFAULT_ARBITER = 'origin';
|
|
319
319
|
|
|
320
320
|
/**
|
|
321
|
-
* **The STAGED-TASKS dir** (
|
|
321
|
+
* **The STAGED-TASKS dir** (spec `staging-pool-position-gate-and-trust-model`,
|
|
322
322
|
* task `pre-backlog-staging-folder-and-promote-step-a`, governing ADR
|
|
323
323
|
* `placement-is-runner-deterministic-humanonly-is-agent-judgement`). The runner
|
|
324
324
|
* lands the tasker's emitted task files HERE, NOT in `work/tasks/ready/`: an item
|
|
@@ -336,7 +336,7 @@ export const STAGED_TASKS_DIR = workFolderRel('tasks-backlog');
|
|
|
336
336
|
* origin, or an `--tasks-land-in ready` operator override). The agent NEVER
|
|
337
337
|
* writes here — it always writes to {@link STAGED_TASKS_DIR}; the runner
|
|
338
338
|
* redirects the emitted files to the resolved destination at integrate-stage
|
|
339
|
-
* time.
|
|
339
|
+
* time. spec US #4 / the governing ADR: the agent cannot self-place into the
|
|
340
340
|
* pool. Task `runner-deterministic-slice-placement-policy-and-precedence`.
|
|
341
341
|
*/
|
|
342
342
|
const POOL_TASKS_DIR = workFolderRel('tasks-ready');
|
|
@@ -369,7 +369,7 @@ function stagedTaskPath(name: string): string {
|
|
|
369
369
|
}
|
|
370
370
|
|
|
371
371
|
/**
|
|
372
|
-
* Run the `do
|
|
372
|
+
* Run the `do spec:<slug>` tasking path end-to-end. Never throws for the expected
|
|
373
373
|
* gate-refused / lock-lost / agent-failed / stale cases — those are returned with
|
|
374
374
|
* the appropriate exit code and outcome. The runner owns all git; the agent only
|
|
375
375
|
* writes task files.
|
|
@@ -389,11 +389,11 @@ export async function performTask(
|
|
|
389
389
|
const doer = options.doer ?? 'agent';
|
|
390
390
|
const lock = options.lock ?? DEFAULT_LOCK_SEAM;
|
|
391
391
|
|
|
392
|
-
// 0. The
|
|
392
|
+
// 0. The spec must exist in the checkout (`work/specs/ready/<slug>.md`) — it is the
|
|
393
393
|
// source the agent tasks + the file the lock holds.
|
|
394
394
|
const specPath = workItemPath(cwd, 'specs-ready', slug);
|
|
395
395
|
if (!existsSync(specPath)) {
|
|
396
|
-
const message = `no
|
|
396
|
+
const message = `no spec '${slug}' found at ${workFolderRel('specs-ready')}/${slug}.md.`;
|
|
397
397
|
note(message);
|
|
398
398
|
return {exitCode: 1, outcome: 'usage-error', slug, message};
|
|
399
399
|
}
|
|
@@ -401,7 +401,7 @@ export async function performTask(
|
|
|
401
401
|
const specFm = parseFrontmatter(specContent);
|
|
402
402
|
|
|
403
403
|
// 1. RESOLVE THE GATE (agent path only). The human path is UNBOUND — a human
|
|
404
|
-
// decides for themselves whether a
|
|
404
|
+
// decides for themselves whether a spec is taskable.
|
|
405
405
|
if (doer === 'agent') {
|
|
406
406
|
const eligibility = resolveAgentGate(
|
|
407
407
|
cwd,
|
|
@@ -452,9 +452,9 @@ export async function performTask(
|
|
|
452
452
|
|
|
453
453
|
// 2b. ONBOARD the agent's tasking work onto a `work/<slug>` BRANCH cut from the
|
|
454
454
|
// freshly-fetched `<arbiter>/main` (task `slice-output-through-integration`).
|
|
455
|
-
// The
|
|
456
|
-
// longer moves it), so the branch's base HOLDS the held
|
|
457
|
-
// stage below moves it `
|
|
455
|
+
// The spec body rests in `work/specs/ready/<slug>.md` on `<arbiter>/main` (the lock no
|
|
456
|
+
// longer moves it), so the branch's base HOLDS the held spec — the lifecycle
|
|
457
|
+
// stage below moves it `specs/ready/ → specs/tasked/` ON THIS BRANCH and the shared integrate core (`--propose`
|
|
458
458
|
// PR / `--merge` main) lands the whole transition, WITHOUT the lock release
|
|
459
459
|
// committing tasks straight to `main`. The agent runs IN-PLACE on this branch
|
|
460
460
|
// (branch ≠ worktree; the isolation seam upgrades it). The HUMAN path stays on
|
|
@@ -463,7 +463,7 @@ export async function performTask(
|
|
|
463
463
|
await switchToWorkBranch(cwd, arbiter, slug, env);
|
|
464
464
|
}
|
|
465
465
|
|
|
466
|
-
// 3. INVOKE THE AGENT with the to-task
|
|
466
|
+
// 3. INVOKE THE AGENT with the to-task spec. It WRITES
|
|
467
467
|
// `work/tasks/backlog/*.md` task files (the STAGED area — NOT `work/tasks/ready/`,
|
|
468
468
|
// which is the agent-eligible pool the runner owns the promotion into; task
|
|
469
469
|
// `pre-backlog-staging-folder-and-promote-step-a`); it does NO git. We
|
|
@@ -472,11 +472,10 @@ export async function performTask(
|
|
|
472
472
|
const before = snapshotStagedTasks(cwd);
|
|
473
473
|
// Also snapshot the POOL `work/tasks/ready/` BEFORE the agent runs: the runner's
|
|
474
474
|
// final commit must scrub any agent writes there (an attempt to self-place into
|
|
475
|
-
// the pool,
|
|
475
|
+
// the pool, spec US #4) before `git add -A` would sweep them in.
|
|
476
476
|
const poolBefore = snapshotPool(cwd);
|
|
477
|
-
//
|
|
478
|
-
//
|
|
479
|
-
// `prdFm.prd` by the expand task).
|
|
477
|
+
// Read the parent-spec self-pointer off `specFm.spec` (populated from the
|
|
478
|
+
// `spec:` key).
|
|
480
479
|
const prompt = buildTaskingSpec(slug, specFm.spec);
|
|
481
480
|
let agent: {ok: boolean; detail?: string};
|
|
482
481
|
try {
|
|
@@ -522,10 +521,10 @@ export async function performTask(
|
|
|
522
521
|
env: agentEnv,
|
|
523
522
|
note,
|
|
524
523
|
});
|
|
525
|
-
// DECOMPOSITION UNCLEAR: emit NO guessed tasks — route the held
|
|
524
|
+
// DECOMPOSITION UNCLEAR: emit NO guessed tasks — route the held spec to
|
|
526
525
|
// needs-attention with the questions as the reason. The lock release amends the
|
|
527
|
-
// `
|
|
528
|
-
// is the stuck lock now — NO folder write; the
|
|
526
|
+
// `spec:<slug>` unified lock `active → stuck` (the tasking needs-attention surface
|
|
527
|
+
// is the stuck lock now — NO folder write; the spec body stays in `work/specs/ready/`).
|
|
529
528
|
if (loopDisposition.outcome === 'decomposition-unclear') {
|
|
530
529
|
const reason = decompositionUnclearReason(
|
|
531
530
|
slug,
|
|
@@ -564,7 +563,7 @@ export async function performTask(
|
|
|
564
563
|
}
|
|
565
564
|
|
|
566
565
|
// 4. The RUNNER commits the COMPLETING transition: drop the produced backlog
|
|
567
|
-
// tasks IN + move the
|
|
566
|
+
// tasks IN + move the spec specs/ready/ -> specs/tasked/ (residence = tasked-ness) — now
|
|
568
567
|
// through the SHARED integrate core (`--propose` PR / `--merge` main), NOT a
|
|
569
568
|
// direct commit to `main`. The agent never does git. (The backlog snapshot is
|
|
570
569
|
// taken AFTER any loop edits, so the runner integrates the IMPROVED tasks,
|
|
@@ -575,7 +574,7 @@ export async function performTask(
|
|
|
575
574
|
// `runner-deterministic-slice-placement-policy-and-precedence`). Resolve which
|
|
576
575
|
// folder the runner lands the emitted tasks in BEFORE handing them to the
|
|
577
576
|
// shared integrate band: precedence `explicit > untrusted-origin ⇒ staging >
|
|
578
|
-
// tasksLandIn > built-in (staging)`, all from unforgeable inputs (the
|
|
577
|
+
// tasksLandIn > built-in (staging)`, all from unforgeable inputs (the spec's
|
|
579
578
|
// stamped `originTrust:` + the resolved per-repo default + the operator's
|
|
580
579
|
// explicit flag). The agent NEVER influences this; it always writes to
|
|
581
580
|
// `work/tasks/backlog/`, and the runner redirects at `stage()` time.
|
|
@@ -603,11 +602,11 @@ export async function performTask(
|
|
|
603
602
|
if (useLock) {
|
|
604
603
|
// READ-STABILITY BACKSTOP (the lock's content-identity check, now owned at the
|
|
605
604
|
// integrate seam): the OUTPUT no longer rides the lock release, so the band
|
|
606
|
-
// below would otherwise rebase a concurrent edit of the held
|
|
607
|
-
// into
|
|
605
|
+
// below would otherwise rebase a concurrent edit of the held spec body CLEANLY
|
|
606
|
+
// into spec/ (a rename+edit merge) while the tasks were cut from the OLD body —
|
|
608
607
|
// the exact silent stale-task drift the lock forbids
|
|
609
|
-
// (`work/notes/observations/tasking-lock-does-not-stabilise-
|
|
610
|
-
// compare the CURRENTLY held `work/
|
|
608
|
+
// (`work/notes/observations/tasking-lock-does-not-stabilise-spec-content.md`). So we
|
|
609
|
+
// compare the CURRENTLY held `work/specs/ready/<slug>.md` blob on the arbiter against
|
|
611
610
|
// the snapshot the lock TOOK (`lockedBlob`); ANY change ⇒ STALE ⇒ fail loud,
|
|
612
611
|
// touch NOTHING (the lock stays held; a human re-tasks or routes to
|
|
613
612
|
// needs-attention). It is the SAME content-identity check `releaseTaskingLock`
|
|
@@ -617,17 +616,17 @@ export async function performTask(
|
|
|
617
616
|
if (stale) {
|
|
618
617
|
const specRel = workItemRel('specs-ready', `${slug}.md`);
|
|
619
618
|
const message =
|
|
620
|
-
`RELEASE CONFLICT for '${slug}': the
|
|
619
|
+
`RELEASE CONFLICT for '${slug}': the spec was edited (${specRel} ` +
|
|
621
620
|
`changed on ${arbiter}/main) while the tasking lock was held. The tasking is ` +
|
|
622
|
-
`STALE — re-task from the edited
|
|
621
|
+
`STALE — re-task from the edited spec or route it to needs-attention. ` +
|
|
623
622
|
`The arbiter was NOT modified (lock still held).`;
|
|
624
623
|
note(message);
|
|
625
624
|
return {exitCode: 4, outcome: 'stale', slug, message};
|
|
626
625
|
}
|
|
627
626
|
|
|
628
627
|
// Route the OUTPUT through the SHARED integrate back-half (task
|
|
629
|
-
// `slice-output-through-integration`): the produced backlog tasks + the
|
|
630
|
-
// lifecycle move (`work/
|
|
628
|
+
// `slice-output-through-integration`): the produced backlog tasks + the spec
|
|
629
|
+
// lifecycle move (`work/specs/ready/ -> work/specs/tasked/`, residence = tasked-ness) integrate
|
|
631
630
|
// via `performIntegration` honoring `--propose` (push the work branch + open a
|
|
632
631
|
// PR, NO `main` touch) / `--merge` (land on `main`). Because the integrate-time
|
|
633
632
|
// args resolve ONCE in the shared core, every `do task:` arg applies here by
|
|
@@ -667,13 +666,13 @@ export async function performTask(
|
|
|
667
666
|
// path's merge-vs-propose decision is the `integration` mode the user typed;
|
|
668
667
|
// `merge` IS the auto-land mode, so a resolved `merge` is never downgraded.
|
|
669
668
|
// The task gate family is `--review`/`--no-review`/`--review-model` only
|
|
670
|
-
// (
|
|
669
|
+
// (spec US #6).
|
|
671
670
|
mode: options.integration ?? 'propose',
|
|
672
671
|
noPR: options.noPR,
|
|
673
672
|
providerInstance: options.providerInstance,
|
|
674
673
|
type: 'tasking',
|
|
675
674
|
lifecycle: {
|
|
676
|
-
// Read the PR title / commit summary from the held
|
|
675
|
+
// Read the PR title / commit summary from the held spec (before it moves).
|
|
677
676
|
titlePath: workItemPath(cwd, 'specs-ready', slug),
|
|
678
677
|
commitTag: 'tasked',
|
|
679
678
|
stage: () =>
|
|
@@ -700,9 +699,9 @@ export async function performTask(
|
|
|
700
699
|
// the review BEFORE the stage/integrate and did NOT integrate the tasks
|
|
701
700
|
// (correct). The CORRECT task-path destination is the SAME needs-attention
|
|
702
701
|
// route the lock release owns for the decomposition-unclear verdict: it amends
|
|
703
|
-
// the `
|
|
702
|
+
// the `spec:<slug>` unified lock `active -> stuck` with the block reason (the
|
|
704
703
|
// tasking needs-attention surface is the stuck lock now — NO folder write; the
|
|
705
|
-
//
|
|
704
|
+
// spec body stays in `work/specs/ready/`). So on a block we route the held spec to
|
|
706
705
|
// needs-attention THROUGH the lock release — the set never lands.
|
|
707
706
|
if (core.outcome === 'review-blocked') {
|
|
708
707
|
const reason = taskGateBlockedReason(slug, core.reviewBlockReason);
|
|
@@ -730,9 +729,9 @@ export async function performTask(
|
|
|
730
729
|
}
|
|
731
730
|
if (core.outcome === 'review-unparseable') {
|
|
732
731
|
// The task-set acceptance gate RAN but its verdict was UNPARSEABLE (malformed
|
|
733
|
-
// JSON). Route the held
|
|
732
|
+
// JSON). Route the held spec to needs-attention through the SAME lock-release
|
|
734
733
|
// seam the block path uses (the tasking needs-attention surface is the stuck
|
|
735
|
-
// `
|
|
734
|
+
// `spec:<slug>` lock; no folder write). It is NOT a block (the gate output was
|
|
736
735
|
// unreadable) — record it as the transient-infra-class re-run signal so the
|
|
737
736
|
// stuck reason reads correctly; nothing landed.
|
|
738
737
|
const reason =
|
|
@@ -761,8 +760,8 @@ export async function performTask(
|
|
|
761
760
|
};
|
|
762
761
|
}
|
|
763
762
|
if (core.outcome === 'completed') {
|
|
764
|
-
// The durable `
|
|
765
|
-
// core (the body moved straight from `work/
|
|
763
|
+
// The durable `specs/ready → specs/tasked` `main` move landed through the shared integrate
|
|
764
|
+
// core (the body moved straight from `work/specs/ready/` — no transient `tasking/`
|
|
766
765
|
// marker). The completing commit is owned by the integrate band, NOT
|
|
767
766
|
// `releaseTaskingLock`, so the unified per-item lock that `acquireTaskingLock`
|
|
768
767
|
// took is released HERE (delete the ref). A
|
|
@@ -780,13 +779,13 @@ export async function performTask(
|
|
|
780
779
|
}
|
|
781
780
|
|
|
782
781
|
// HUMAN, no-lock path: the human commits on `main` directly (the runner does
|
|
783
|
-
// not own the human's git). We report the produced tasks; moving the
|
|
784
|
-
// `work/
|
|
782
|
+
// not own the human's git). We report the produced tasks; moving the spec into
|
|
783
|
+
// `work/specs/tasked/` (residence = tasked-ness) and committing is the human's to
|
|
785
784
|
// do, as with the human `complete`.
|
|
786
785
|
const message =
|
|
787
786
|
`Tasked '${slug}' -> ${emitted.length} backlog task` +
|
|
788
787
|
`${emitted.length === 1 ? '' : 's'} (human path, no lock). Inspect + commit ` +
|
|
789
|
-
`the produced files (and move the
|
|
788
|
+
`the produced files (and move the spec into work/specs/tasked/) yourself.`;
|
|
790
789
|
note(message);
|
|
791
790
|
return {
|
|
792
791
|
exitCode: 0,
|
|
@@ -828,11 +827,11 @@ function releaseFailureToResult(
|
|
|
828
827
|
* (propose pushed the work branch + opened a PR / merge landed on `main`) the
|
|
829
828
|
* tasking is `tasked`. The band's FAILURE outcomes are reported on the tasking
|
|
830
829
|
* contract: a `rebase-conflict` against a concurrently-advanced `main` maps to
|
|
831
|
-
* `stale` (exit 4) — the tasking analogue of "the held
|
|
830
|
+
* `stale` (exit 4) — the tasking analogue of "the held spec moved under us"; a
|
|
832
831
|
* a `gate-failed` cannot occur (the tasking path passes `skipVerify`) but maps to
|
|
833
832
|
* a usage error defensively. A `review-blocked` (the task-SET ACCEPTANCE GATE
|
|
834
833
|
* blocked the set, task `slice-acceptance-gate`) is handled by `performTask`
|
|
835
|
-
* BEFORE this mapper — it routes the held
|
|
834
|
+
* BEFORE this mapper — it routes the held spec to needs-attention via
|
|
836
835
|
* the lock release (the task-path needs-attention route) — so it never reaches
|
|
837
836
|
* here; it is mapped defensively to a usage error if it ever does.
|
|
838
837
|
*/
|
|
@@ -853,7 +852,7 @@ function integrationToTaskResult(
|
|
|
853
852
|
const message =
|
|
854
853
|
`Tasked '${slug}' -> ${emitted.length} backlog task` +
|
|
855
854
|
`${emitted.length === 1 ? '' : 's'}; the runner integrated the transition ` +
|
|
856
|
-
`through the shared core (moved work/
|
|
855
|
+
`through the shared core (moved work/specs/ready/ -> work/specs/tasked/, the ` +
|
|
857
856
|
`tasked resting state) and ${landed}.`;
|
|
858
857
|
return {exitCode: 0, outcome: 'tasked', slug, emitted, loop, message};
|
|
859
858
|
}
|
|
@@ -865,7 +864,7 @@ function integrationToTaskResult(
|
|
|
865
864
|
message:
|
|
866
865
|
core.reason ??
|
|
867
866
|
`Integrating the tasking of '${slug}' conflicted against the latest ` +
|
|
868
|
-
`${slug} main — the tasking is stale; re-task from the current
|
|
867
|
+
`${slug} main — the tasking is stale; re-task from the current spec.`,
|
|
869
868
|
};
|
|
870
869
|
}
|
|
871
870
|
return {
|
|
@@ -881,9 +880,9 @@ function integrationToTaskResult(
|
|
|
881
880
|
/**
|
|
882
881
|
* ONBOARD the tasking work onto a `work/<slug>` branch cut from the freshly-
|
|
883
882
|
* fetched `<arbiter>/main` (task `slice-output-through-integration`). Called
|
|
884
|
-
* AFTER the tasking lock is held, so the branch's base HOLDS the
|
|
885
|
-
* `work/
|
|
886
|
-
* it `
|
|
883
|
+
* AFTER the tasking lock is held, so the branch's base HOLDS the spec in
|
|
884
|
+
* `work/specs/ready/` (the lock no longer moves the body) — the lifecycle stage then moves
|
|
885
|
+
* it `specs/ready/ -> specs/tasked/` ON THIS BRANCH and the shared integrate core lands it. A
|
|
887
886
|
* pre-existing local `work/<slug>` (a re-run) is force-recreated off fresh main.
|
|
888
887
|
* The agent runs in-place on this branch (branch ≠ worktree).
|
|
889
888
|
*/
|
|
@@ -895,7 +894,7 @@ async function switchToWorkBranch(
|
|
|
895
894
|
): Promise<void> {
|
|
896
895
|
// The tasking path is the parent-spec namespace (`do spec:<slug>`): the branch
|
|
897
896
|
// is `work/spec-<slug>`, distinct from a same-slug task-build's `work/task-<slug>`.
|
|
898
|
-
// MIGRATE step (
|
|
897
|
+
// MIGRATE step (spec `prd-to-spec-vocabulary-cutover-and-migration-command`):
|
|
899
898
|
// MINT the work-branch under the `spec` namespace token (`workBranchRef` still
|
|
900
899
|
// parses the legacy `work/prd-<slug>` form, so in-flight branches keep resolving).
|
|
901
900
|
const branch = workBranchRef('spec', slug);
|
|
@@ -910,8 +909,8 @@ async function switchToWorkBranch(
|
|
|
910
909
|
/**
|
|
911
910
|
* The READ-STABILITY content-identity STALE CHECK (the lock's backstop, owned at
|
|
912
911
|
* the integrate seam now that the OUTPUT no longer rides the lock release): true
|
|
913
|
-
* iff the CURRENTLY held `work/
|
|
914
|
-
* from the snapshot the lock TOOK (`lockedBlob`, read from `work/
|
|
912
|
+
* iff the CURRENTLY held `work/specs/ready/<slug>.md` blob on `<arbiter>/main` DIFFERS
|
|
913
|
+
* from the snapshot the lock TOOK (`lockedBlob`, read from `work/specs/ready/<slug>.md` at
|
|
915
914
|
* acquire). ANY change = a concurrent edit under the lock = the tasking is STALE.
|
|
916
915
|
* Stronger than a textual rebase conflict (which a rename+edit merge can apply
|
|
917
916
|
* CLEANLY). When `lockedBlob` is absent (never, in production) it reads as
|
|
@@ -933,7 +932,7 @@ async function heldSpecIsStale(
|
|
|
933
932
|
cwd,
|
|
934
933
|
env,
|
|
935
934
|
);
|
|
936
|
-
// The
|
|
935
|
+
// The spec being absent (already tasked/moved) is NOT this check's concern
|
|
937
936
|
// (the integrate's rebase/push surfaces that); only a CHANGED held blob is stale.
|
|
938
937
|
if (held.status !== 0) {
|
|
939
938
|
return false;
|
|
@@ -943,18 +942,18 @@ async function heldSpecIsStale(
|
|
|
943
942
|
|
|
944
943
|
/**
|
|
945
944
|
* STAGE the tasking lifecycle into the index on the `work/<slug>` branch (the
|
|
946
|
-
* {@link performIntegration} lifecycle seam): move the held
|
|
947
|
-
* `git mv work/
|
|
945
|
+
* {@link performIntegration} lifecycle seam): move the held spec
|
|
946
|
+
* `git mv work/specs/ready/<slug>.md -> work/specs/tasked/<slug>.md` (the TASKED resting
|
|
948
947
|
* state — the build-machine `done/` analogue, the SOURCE OF TRUTH for tasked-ness),
|
|
949
948
|
* and write+`git add` the produced `work/tasks/backlog/*.md` files. The band's subsequent
|
|
950
949
|
* `git add -A` + atomic commit folds this AND the agent's uncommitted backlog writes
|
|
951
950
|
* into ONE runner-owned commit (the agent never does git).
|
|
952
951
|
*
|
|
953
|
-
* TASK `prd-sliced-folder-step-a` (
|
|
954
|
-
* destination is `work/
|
|
952
|
+
* TASK `prd-sliced-folder-step-a` (spec `slicing-coherence` US #8): the lifecycle
|
|
953
|
+
* destination is `work/specs/tasked/` (NOT back to `work/specs/ready/`) — `specs/tasked/`
|
|
955
954
|
* residence IS tasked-ness (like `done/` for tasks, with no `done:` marker). The
|
|
956
955
|
* `tasked:` frontmatter marker was removed entirely in `remove-sliced-marker-step-b`
|
|
957
|
-
* (sequenced last): residence in `work/
|
|
956
|
+
* (sequenced last): residence in `work/specs/tasked/` is now the sole signal.
|
|
958
957
|
*/
|
|
959
958
|
async function stageTaskingLifecycle(params: {
|
|
960
959
|
cwd: string;
|
|
@@ -965,7 +964,7 @@ async function stageTaskingLifecycle(params: {
|
|
|
965
964
|
* The runner-resolved destination folder (task
|
|
966
965
|
* `runner-deterministic-slice-placement-policy-and-precedence`). Computed
|
|
967
966
|
* ONCE in `performTask` via the shared {@link resolvePlacement} from the
|
|
968
|
-
*
|
|
967
|
+
* spec's `originTrust:` stamp + the configured `tasksLandIn` default + the
|
|
969
968
|
* operator's explicit override, then passed in here — so the call site sees
|
|
970
969
|
* exactly where the emitted tasks landed (the placement decision is not
|
|
971
970
|
* buried in the stage closure).
|
|
@@ -993,10 +992,10 @@ async function stageTaskingLifecycle(params: {
|
|
|
993
992
|
const spec = workItemRel('specs-ready', `${slug}.md`);
|
|
994
993
|
const specTasked = workItemRel('specs-tasked', `${slug}.md`);
|
|
995
994
|
// PROPAGATE the origin-trust PROVENANCE (task
|
|
996
|
-
// `untrusted-origin-forces-build-propose`): read the held
|
|
995
|
+
// `untrusted-origin-forces-build-propose`): read the held spec's `origin`/
|
|
997
996
|
// `originTrust` stamp BEFORE the move, so each emitted task can carry it. A
|
|
998
997
|
// task's risk is its BUILD; the stamp must reach the task so the build
|
|
999
|
-
// transition can force `propose` for untrusted-origin work. An UNSTAMPED
|
|
998
|
+
// transition can force `propose` for untrusted-origin work. An UNSTAMPED spec (a
|
|
1000
999
|
// human/local-authored one ⇒ trusted) propagates nothing — the normal path is
|
|
1001
1000
|
// untouched.
|
|
1002
1001
|
const specAbs = join(cwd, spec);
|
|
@@ -1005,19 +1004,19 @@ async function stageTaskingLifecycle(params: {
|
|
|
1005
1004
|
: {origin: undefined, originTrust: undefined};
|
|
1006
1005
|
if (placementReason === 'untrusted-origin') {
|
|
1007
1006
|
note(
|
|
1008
|
-
`Untrusted-origin
|
|
1007
|
+
`Untrusted-origin spec '${slug}': forcing the emitted tasks STAGED ` +
|
|
1009
1008
|
`(${placementDir}/) regardless of tasksLandIn (a human promotes ` +
|
|
1010
1009
|
'them into work/tasks/ready/). Pass --tasks-land-in <where> to override.',
|
|
1011
1010
|
);
|
|
1012
1011
|
}
|
|
1013
|
-
// Move the held
|
|
1014
|
-
// source of truth, like done/ for tasks). This is the DURABLE `
|
|
1012
|
+
// Move the held spec specs/ready/ -> specs/tasked/ (the TASKED resting state — folder =
|
|
1013
|
+
// source of truth, like done/ for tasks). This is the DURABLE `specs/ready → specs/tasked`
|
|
1015
1014
|
// success move, owned by THIS transition's commit (the lock no longer moved the
|
|
1016
|
-
// body, so the source is `work/
|
|
1015
|
+
// body, so the source is `work/specs/ready/`, never `work/tasking/`).
|
|
1017
1016
|
mkdirSync(dirname(join(cwd, specTasked)), {recursive: true});
|
|
1018
1017
|
await gitHard(['mv', spec, specTasked], cwd, env);
|
|
1019
1018
|
await gitHard(['add', '--', specTasked], cwd, env);
|
|
1020
|
-
// **POOL-PLACEMENT FENCE (
|
|
1019
|
+
// **POOL-PLACEMENT FENCE (spec US #4 / governing ADR
|
|
1021
1020
|
// `placement-is-runner-deterministic-humanonly-is-agent-judgement`).** The
|
|
1022
1021
|
// agent ALWAYS writes to the STAGING folder (`work/tasks/backlog/`); the POOL
|
|
1023
1022
|
// (`work/tasks/ready/`) is the agent-eligible pool the runner owns the promotion
|
|
@@ -1055,7 +1054,7 @@ async function stageTaskingLifecycle(params: {
|
|
|
1055
1054
|
* Revert any change/addition the agent made to the POOL `work/tasks/ready/` during a
|
|
1056
1055
|
* tasking run. The agent's STAGING folder is `work/tasks/backlog/`; a write to the
|
|
1057
1056
|
* pool is an attempt to self-place into the agent-eligible pool the runner owns
|
|
1058
|
-
* the promotion into (
|
|
1057
|
+
* the promotion into (spec US #4 / governing ADR). Compared to the `poolBefore`
|
|
1059
1058
|
* snapshot (the branch-base state of `work/tasks/ready/`, taken BEFORE the agent
|
|
1060
1059
|
* ran), any new file is removed from the worktree and any changed file is
|
|
1061
1060
|
* checked back out to HEAD — so the subsequent `git add -A` cannot land it. The
|
|
@@ -1123,7 +1122,7 @@ async function gitHard(
|
|
|
1123
1122
|
/**
|
|
1124
1123
|
* Build the needs-attention REASON for a task-SET ACCEPTANCE GATE block (task
|
|
1125
1124
|
* `slice-acceptance-gate`): the fresh-context review of the produced set returned
|
|
1126
|
-
* `block`, so the
|
|
1125
|
+
* `block`, so the spec is routed to `work/needs-attention/` with the review's
|
|
1127
1126
|
* blocking findings as the body prose and NO tasks landed. Takes the core's
|
|
1128
1127
|
* structured `reviewBlockReason` (the gate's blocking findings); falls back to a
|
|
1129
1128
|
* generic line when absent. DISTINCT from the improver loop's
|
|
@@ -1135,20 +1134,20 @@ function taskGateBlockedReason(
|
|
|
1135
1134
|
): string {
|
|
1136
1135
|
const head =
|
|
1137
1136
|
`The task acceptance gate (fresh-context review of the produced SET) blocked ` +
|
|
1138
|
-
`'${slug}'. The
|
|
1137
|
+
`'${slug}'. The spec is routed to needs-attention with no tasks landed; a human ` +
|
|
1139
1138
|
`must resolve the blocking findings, then re-task.`;
|
|
1140
1139
|
return findingsReason ? `${head}\n\n${findingsReason}` : head;
|
|
1141
1140
|
}
|
|
1142
1141
|
|
|
1143
1142
|
/**
|
|
1144
1143
|
* Build the needs-attention REASON for a decomposition-unclear loop verdict (the
|
|
1145
|
-
*
|
|
1146
|
-
* tasks). Prose only — recorded as the
|
|
1144
|
+
* spec is routed to `work/needs-attention/` with these open questions, no guessed
|
|
1145
|
+
* tasks). Prose only — recorded as the spec's needs-attention body block.
|
|
1147
1146
|
*/
|
|
1148
1147
|
function decompositionUnclearReason(slug: string, questions: string[]): string {
|
|
1149
1148
|
const head =
|
|
1150
1149
|
`The tasker review→edit loop could not converge on a sound decomposition of ` +
|
|
1151
|
-
`'${slug}' (--tasker-loop-max exhausted with unresolved blockers). The
|
|
1150
|
+
`'${slug}' (--tasker-loop-max exhausted with unresolved blockers). The spec is routed ` +
|
|
1152
1151
|
`to needs-attention with no guessed tasks; a human must resolve:`;
|
|
1153
1152
|
const body =
|
|
1154
1153
|
questions.length > 0
|
|
@@ -1210,8 +1209,8 @@ function appendQuestionsBlock(content: string, questions: string[]): string {
|
|
|
1210
1209
|
/**
|
|
1211
1210
|
* Resolve the AGENT tasking gate for `slug`: the pure predicate
|
|
1212
1211
|
* (`needsAnswers !== true && humanOnly !== true && autoTask`) plus the
|
|
1213
|
-
* cross-
|
|
1214
|
-
* the
|
|
1212
|
+
* cross-spec `taskedAfter` ordering, resolved against `work/specs/tasked/` residence of
|
|
1213
|
+
* the specs present in the checkout.
|
|
1215
1214
|
*/
|
|
1216
1215
|
function resolveAgentGate(
|
|
1217
1216
|
cwd: string,
|
|
@@ -1230,7 +1229,7 @@ function resolveAgentGate(
|
|
|
1230
1229
|
});
|
|
1231
1230
|
}
|
|
1232
1231
|
|
|
1233
|
-
/** Build an HONEST gate-refusal message naming WHY the agent skipped the
|
|
1232
|
+
/** Build an HONEST gate-refusal message naming WHY the agent skipped the spec. */
|
|
1234
1233
|
function gateRefusalReason(
|
|
1235
1234
|
slug: string,
|
|
1236
1235
|
specFm: {humanOnly?: boolean; needsAnswers?: boolean},
|
|
@@ -1239,15 +1238,15 @@ function gateRefusalReason(
|
|
|
1239
1238
|
): string {
|
|
1240
1239
|
const reasons: string[] = [];
|
|
1241
1240
|
if (specFm.humanOnly === true) {
|
|
1242
|
-
reasons.push('the
|
|
1241
|
+
reasons.push('the spec is humanOnly (a human must drive its tasking)');
|
|
1243
1242
|
}
|
|
1244
1243
|
if (specFm.needsAnswers === true) {
|
|
1245
1244
|
reasons.push(
|
|
1246
|
-
'the
|
|
1245
|
+
'the spec has needsAnswers (open questions block auto-tasking)',
|
|
1247
1246
|
);
|
|
1248
1247
|
}
|
|
1249
1248
|
// The autoTask POLICY only refuses on the NON-explicit (auto-pick pool) path:
|
|
1250
|
-
// an explicitly-named `do
|
|
1249
|
+
// an explicitly-named `do spec:<slug>` is authorized by the naming itself (the
|
|
1251
1250
|
// build path's autoBuild precedent), so the policy is never the reason there.
|
|
1252
1251
|
if (
|
|
1253
1252
|
options.explicit !== true &&
|
|
@@ -1259,7 +1258,7 @@ function gateRefusalReason(
|
|
|
1259
1258
|
}
|
|
1260
1259
|
if (!eligibility.taskedAfter.satisfied) {
|
|
1261
1260
|
reasons.push(
|
|
1262
|
-
`taskedAfter
|
|
1261
|
+
`taskedAfter spec(s) not yet tasked: ${eligibility.taskedAfter.missing.join(', ')}`,
|
|
1263
1262
|
);
|
|
1264
1263
|
}
|
|
1265
1264
|
const why =
|
|
@@ -1268,12 +1267,12 @@ function gateRefusalReason(
|
|
|
1268
1267
|
}
|
|
1269
1268
|
|
|
1270
1269
|
/**
|
|
1271
|
-
* Read the set of slugs whose
|
|
1272
|
-
* in `work/
|
|
1273
|
-
* /
|
|
1270
|
+
* Read the set of slugs whose specs are already TASKED in this checkout — RESIDENCE
|
|
1271
|
+
* in `work/specs/tasked/` (the tasked resting state, task `prd-sliced-folder-step-a`
|
|
1272
|
+
* / spec `slicing-coherence` US #9), the build-machine `done/` analogue. The FOLDER
|
|
1274
1273
|
* is the source of truth; the `tasked:` frontmatter marker was removed entirely in
|
|
1275
1274
|
* `remove-sliced-marker-step-b` and is NOT consulted. So `taskedAfter` resolves
|
|
1276
|
-
* against `
|
|
1275
|
+
* against `specs/tasked/` residence
|
|
1277
1276
|
* (mirroring `blockedBy` -> `done/`). A missing folder reads as empty. The slug is
|
|
1278
1277
|
* read from each file's frontmatter `slug:`, falling back to the filename — the same
|
|
1279
1278
|
* shape the task readers use.
|
|
@@ -1292,7 +1291,7 @@ function readTaskedSlugs(cwd: string): Set<string> {
|
|
|
1292
1291
|
/**
|
|
1293
1292
|
* Build the tasking PROMPT: instruct a fresh-context agent to apply the
|
|
1294
1293
|
* **tasking discipline** (`work/protocol/TASKING-PROTOCOL.md`) to the held
|
|
1295
|
-
*
|
|
1294
|
+
* spec at `work/specs/ready/<slug>.md` and to EMIT tracer-bullet vertical
|
|
1296
1295
|
* tasks under `work/tasks/backlog/`. The discipline body (the tracer-bullet
|
|
1297
1296
|
* rules, the two-axis gate guidance, the confidence check, file-orthogonality,
|
|
1298
1297
|
* the emitted task shape) lives in `TASKING-PROTOCOL.md` — NOT inlined here
|
|
@@ -1301,24 +1300,26 @@ function readTaskedSlugs(cwd: string): Set<string> {
|
|
|
1301
1300
|
* task-template.md`); the doc DESCRIBES it (D2).
|
|
1302
1301
|
*
|
|
1303
1302
|
* This builder owns ONLY the PER-BUILDER framing: who you are (a fresh-context
|
|
1304
|
-
* tasker for ONE
|
|
1303
|
+
* tasker for ONE spec), where the held spec is, where the emitted task files
|
|
1305
1304
|
* MUST land (the staging folder, never the pool), and the runner-owns-git
|
|
1306
1305
|
* boundary on the agent path. The shared discipline body is NOT duplicated
|
|
1307
1306
|
* here.
|
|
1308
1307
|
*/
|
|
1309
1308
|
function buildTaskingSpec(slug: string, _spec: string | undefined): string {
|
|
1309
|
+
const specReady = workFolderRel('specs-ready');
|
|
1310
|
+
const specTasked = workFolderRel('specs-tasked');
|
|
1310
1311
|
return [
|
|
1311
|
-
`You are a FRESH-CONTEXT tasker for the
|
|
1312
|
+
`You are a FRESH-CONTEXT tasker for the spec \`${specReady}/${slug}.md\`.`,
|
|
1312
1313
|
`Apply the tasking discipline defined in \`work/protocol/TASKING-PROTOCOL.md\``,
|
|
1313
1314
|
`(the in-band, protocol-native tasking protocol every set-up repo carries; the`,
|
|
1314
|
-
`human-facing pointer is \`skills/to-task/SKILL.md\`) to this ONE
|
|
1315
|
+
`human-facing pointer is \`skills/to-task/SKILL.md\`) to this ONE spec:`,
|
|
1315
1316
|
`decompose it into independently-grabbable, tracer-bullet vertical tasks.`,
|
|
1316
|
-
`Read the
|
|
1317
|
+
`Read the spec fully first.`,
|
|
1317
1318
|
``,
|
|
1318
1319
|
`The discipline rules — the tracer-bullet test, the two-axis gate guidance`,
|
|
1319
1320
|
`(\`humanOnly\` is NARROW; \`needsAnswers\` flags genuine uncertainty), the`,
|
|
1320
1321
|
`confidence check that REPLACES the human-quiz step when no human is present,`,
|
|
1321
|
-
`the file-orthogonality preference, the
|
|
1322
|
+
`the file-orthogonality preference, the spec-vs-task gate disjointness, and`,
|
|
1322
1323
|
`the emitted task shape — ALL live in that doc. Read them there.`,
|
|
1323
1324
|
``,
|
|
1324
1325
|
`No human is present, so apply the CONFIDENCE CHECK (\`TASKING-PROTOCOL.md\``,
|
|
@@ -1326,7 +1327,7 @@ function buildTaskingSpec(slug: string, _spec: string | undefined): string {
|
|
|
1326
1327
|
`granularity, dependency order, a gate, or a seam is genuinely unresolved, set`,
|
|
1327
1328
|
`\`needsAnswers: true\` on the specific uncertain task (questions in its body)`,
|
|
1328
1329
|
`rather than guessing — or, if the whole decomposition is unclear, stop and`,
|
|
1329
|
-
`route the
|
|
1330
|
+
`route the spec to needs-attention with the questions.`,
|
|
1330
1331
|
``,
|
|
1331
1332
|
`WRITE EVERY emitted task file under \`${STAGED_TASKS_DIR}/\` (the STAGING folder)`,
|
|
1332
1333
|
`— NEVER \`work/tasks/ready/\`. \`work/tasks/ready/\` is the agent-eligible POOL and`,
|
|
@@ -1334,12 +1335,12 @@ function buildTaskingSpec(slug: string, _spec: string | undefined): string {
|
|
|
1334
1335
|
`folder is \`work/tasks/backlog/\`. A write outside the staging folder is dropped`,
|
|
1335
1336
|
`by the runner-deterministic placement resolver.`,
|
|
1336
1337
|
``,
|
|
1337
|
-
`Set each task's \`
|
|
1338
|
-
`link back to the
|
|
1338
|
+
`Set each task's \`spec:\` field to the source spec slug (\`${slug}\`) so the`,
|
|
1339
|
+
`link back to the spec survives.`,
|
|
1339
1340
|
``,
|
|
1340
1341
|
`Do NOT perform any git operations — do not stage, commit, push, or move any`,
|
|
1341
1342
|
`files. The RUNNER owns every git-state transition (it commits the produced`,
|
|
1342
|
-
`tasks, releases the tasking lock, and moves the
|
|
1343
|
+
`tasks, releases the tasking lock, and moves the spec into \`${specTasked}/\`).`,
|
|
1343
1344
|
].join('\n');
|
|
1344
1345
|
}
|
|
1345
1346
|
|