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/needs-attention.ts
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* The **needs-attention mechanism** (ADR `ledger-status-on-per-item-lock-refs`;
|
|
33
|
-
*
|
|
33
|
+
* spec `ledger-status-per-item-lock-refs`; ADR §12 for the original folder model).
|
|
34
34
|
* Every "couldn't finish, a human must look" outcome (a failed acceptance gate
|
|
35
35
|
* (red `verify`), a rebase/merge conflict (ADR §10), a task the agent reported
|
|
36
36
|
* too ambiguous to build, a timeout, or a rejected review) resolves to ONE
|
|
@@ -81,7 +81,7 @@ export interface RouteToNeedsAttentionOptions {
|
|
|
81
81
|
/**
|
|
82
82
|
* The work branch to push to the arbiter (the RECOVERABLE half — see the seam
|
|
83
83
|
* docstring). DEFAULT `work/<slug>`: the build-bounce branch the wip/move
|
|
84
|
-
* commits landed on. A tasking bounce passes its own branch (`work/
|
|
84
|
+
* commits landed on. A tasking bounce passes its own branch (`work/specs/
|
|
85
85
|
* ready-<slug>`). The supplied branch MUST be the one HEAD is on (the branch the
|
|
86
86
|
* wip/move commits landed on) — NEVER a default that differs from HEAD; a
|
|
87
87
|
* caller NOT checked out on the work branch (e.g. a temp branch off main) must
|
|
@@ -259,7 +259,7 @@ export interface SurfaceToNeedsAttentionResult {
|
|
|
259
259
|
* (an unreachable arbiter leaves the local branch standing — recovery
|
|
260
260
|
* degrades, never crashes the bounce; retried with bounded backoff on an
|
|
261
261
|
* outage), BRANCH-PARAMETERISED (default `work/<slug>`; an explicit `branch`
|
|
262
|
-
* overrides — the tasking bounce passes `work/
|
|
262
|
+
* overrides — the tasking bounce passes `work/specs/ready-<slug>`; `pushBranch: false`
|
|
263
263
|
* ⇒ push NOTHING), and EMPTINESS-GUARDED (a branch with no commits beyond
|
|
264
264
|
* main, or an absent branch, is skipped). The branch MUST be the one HEAD is
|
|
265
265
|
* on. The work-branch push is NOT a `main` write.
|
|
@@ -316,7 +316,7 @@ export async function routeToNeedsAttention(
|
|
|
316
316
|
let pushError: string | undefined;
|
|
317
317
|
if (options.arbiter && options.pushBranch !== false) {
|
|
318
318
|
// DEFAULT to the task-namespaced build-bounce branch; a non-task caller
|
|
319
|
-
// (the tasking bounce) passes its own `work/
|
|
319
|
+
// (the tasking bounce) passes its own `work/specs/ready-<slug>` via `branch`.
|
|
320
320
|
const branch = options.branch ?? workBranchRef('task', slug);
|
|
321
321
|
if (branchAheadOf(cwd, branch, 'main', env)) {
|
|
322
322
|
const arbiter = options.arbiter;
|
|
@@ -652,7 +652,7 @@ export async function returnToBacklog(
|
|
|
652
652
|
}
|
|
653
653
|
|
|
654
654
|
/**
|
|
655
|
-
* **Promote a STAGED task into the agent-eligible pool** (
|
|
655
|
+
* **Promote a STAGED task into the agent-eligible pool** (spec
|
|
656
656
|
* `staging-pool-position-gate-and-trust-model`, task
|
|
657
657
|
* `pre-backlog-staging-folder-and-promote-step-a`, governing ADR
|
|
658
658
|
* `placement-is-runner-deterministic-humanonly-is-agent-judgement`). Moves
|
|
@@ -733,7 +733,7 @@ export async function promoteFromPreBacklog(
|
|
|
733
733
|
// arbiter's TRUTH. A fetch, not a checkout — the working tree is untouched.
|
|
734
734
|
await gitSoftAsync(['fetch', '--quiet', arbiter], cwd, env);
|
|
735
735
|
|
|
736
|
-
// UNIFIED PER-ITEM LOCK around the CAS window (
|
|
736
|
+
// UNIFIED PER-ITEM LOCK around the CAS window (spec
|
|
737
737
|
// `staging-surface-and-apply-promote-safety`, task
|
|
738
738
|
// `f3b-promote-takes-per-item-advancing-lock`): promote and apply BOTH key onto
|
|
739
739
|
// the item's `refs/dorfl/lock/<entry>` ref with `action: advance` (the
|
|
@@ -850,34 +850,34 @@ export async function promoteFromPreBacklog(
|
|
|
850
850
|
}
|
|
851
851
|
|
|
852
852
|
/**
|
|
853
|
-
* **Promote a STAGED
|
|
853
|
+
* **Promote a STAGED spec into the auto-task pool** (spec
|
|
854
854
|
* `staging-pool-position-gate-and-trust-model`, task
|
|
855
855
|
* `pre-prd-staging-pool-split-and-untrusted-prd-placement`, governing ADR
|
|
856
|
-
* `placement-is-runner-deterministic-humanonly-is-agent-judgement`). The
|
|
856
|
+
* `placement-is-runner-deterministic-humanonly-is-agent-judgement`). The spec
|
|
857
857
|
* twin of {@link promoteFromPreBacklog}: moves
|
|
858
|
-
* `work/
|
|
858
|
+
* `work/specs/proposed/<slug>.md → work/specs/ready/<slug>.md` as a durable `main` move
|
|
859
859
|
* (tree-less CAS via {@link runTreelessLedgerMove}). After this transition
|
|
860
|
-
* the
|
|
860
|
+
* the spec is in the auto-task POOL and eligible to be auto-tasked (subject
|
|
861
861
|
* to the existing `autoTask`/`humanOnly`/`needsAnswers`/`taskedAfter` gates,
|
|
862
862
|
* which are UNCHANGED — the staging/pool split changes only WHICH folder is
|
|
863
863
|
* the auto-task pool, not the gates).
|
|
864
864
|
*
|
|
865
865
|
* **RUNNER/human-owned.** There is no agent-facing path that performs this:
|
|
866
|
-
* `intake`'s `prd` dispatch lands the
|
|
866
|
+
* `intake`'s `prd` dispatch lands the spec STAGED in `work/specs/proposed/` (the
|
|
867
867
|
* runner's deterministic placement decision), and only a runner/human
|
|
868
868
|
* invocation moves it into the pool. The agent does no git here, as
|
|
869
869
|
* everywhere; this function is not reachable from any agent surface.
|
|
870
870
|
*
|
|
871
871
|
* Storage-agnostic + tree-less, exactly like {@link promoteFromPreBacklog}:
|
|
872
872
|
* cwd index/HEAD/working tree are never touched, an arbiter remote is
|
|
873
|
-
* REQUIRED, and "not in
|
|
873
|
+
* REQUIRED, and "not in specs/proposed/" / contention-exhausted cases are returned
|
|
874
874
|
* (NEVER thrown) via `{moved: false, reasonNotMoved}` so callers branch
|
|
875
875
|
* cleanly. Idempotent: re-running after the move LANDED is a no-op success.
|
|
876
876
|
*/
|
|
877
877
|
export interface PromoteFromPreSpecOptions {
|
|
878
878
|
/** The working clone the move is originated from (origin source only; never written). */
|
|
879
879
|
cwd: string;
|
|
880
|
-
/** The slug of the staged
|
|
880
|
+
/** The slug of the staged spec to promote into the pool. */
|
|
881
881
|
slug: string;
|
|
882
882
|
/** The arbiter remote the promotion is CAS-published to. REQUIRED. */
|
|
883
883
|
arbiter: string;
|
|
@@ -888,11 +888,11 @@ export interface PromoteFromPreSpecOptions {
|
|
|
888
888
|
}
|
|
889
889
|
|
|
890
890
|
export interface PromoteFromPreSpecResult {
|
|
891
|
-
/** True iff the staged
|
|
891
|
+
/** True iff the staged spec was moved into the pool + committed. */
|
|
892
892
|
moved: boolean;
|
|
893
893
|
/** When `moved`, the committed transition message. */
|
|
894
894
|
commitMessage?: string;
|
|
895
|
-
/** When NOT moved, why (no such
|
|
895
|
+
/** When NOT moved, why (no such specs/proposed item, already in specs/ready/, contention). */
|
|
896
896
|
reasonNotMoved?: string;
|
|
897
897
|
}
|
|
898
898
|
|
|
@@ -927,14 +927,14 @@ export async function promoteFromPreSpec(
|
|
|
927
927
|
await gitSoftAsync(['fetch', '--quiet', arbiter], cwd, env);
|
|
928
928
|
|
|
929
929
|
// UNIFIED PER-ITEM LOCK around the CAS window — symmetric with
|
|
930
|
-
// {@link promoteFromPreBacklog} (
|
|
931
|
-
// task `f3b-promote-takes-per-item-advancing-lock`, decisive
|
|
930
|
+
// {@link promoteFromPreBacklog} (spec `staging-surface-and-apply-promote-safety`,
|
|
931
|
+
// task `f3b-promote-takes-per-item-advancing-lock`, decisive spec q4 answer:
|
|
932
932
|
// specs share the apply×promote mutual-exclusion fix with tasks). The lock
|
|
933
933
|
// keys on `spec:${slug}` (a distinct ref from a task with the same slug, via
|
|
934
934
|
// {@link lockEntryFor}'s `<type>-<slug>` encoding), with `action: advance` —
|
|
935
935
|
// the SAME action an apply for a spec would take — so spec promote and spec
|
|
936
936
|
// apply on the same item are mutually exclusive by construction. MIGRATE step
|
|
937
|
-
// (
|
|
937
|
+
// (spec `prd-to-spec-vocabulary-cutover-and-migration-command`): the lock
|
|
938
938
|
// identity is `spec:${slug}` to match the `spec-<slug>` entry the tasking/apply
|
|
939
939
|
// path now acquires (`tasking.ts` releases under `spec:${slug}`); a stale
|
|
940
940
|
// `prd:${slug}` here would key a DIFFERENT ref and break the mutual exclusion.
|
|
@@ -1009,7 +1009,7 @@ export async function promoteFromPreSpec(
|
|
|
1009
1009
|
base,
|
|
1010
1010
|
sourceRel,
|
|
1011
1011
|
destRel,
|
|
1012
|
-
// The body is carried byte-for-byte from
|
|
1012
|
+
// The body is carried byte-for-byte from specs/proposed into the pool —
|
|
1013
1013
|
// promotion is a placement decision, not a content transform.
|
|
1014
1014
|
transformBody: (body) => body,
|
|
1015
1015
|
commitMessage,
|
|
@@ -1020,14 +1020,14 @@ export async function promoteFromPreSpec(
|
|
|
1020
1020
|
});
|
|
1021
1021
|
if (moved) {
|
|
1022
1022
|
note(
|
|
1023
|
-
`Promoted
|
|
1023
|
+
`Promoted spec '${slug}' from specs/proposed to specs/ready (auto-taskable).`,
|
|
1024
1024
|
);
|
|
1025
1025
|
return {moved: true, commitMessage};
|
|
1026
1026
|
}
|
|
1027
1027
|
|
|
1028
1028
|
const message =
|
|
1029
1029
|
`promote for '${slug}': the arbiter's main kept moving (contended) ` +
|
|
1030
|
-
`after ${TREELESS_CONTENTION_ATTEMPTS} attempts — item left in
|
|
1030
|
+
`after ${TREELESS_CONTENTION_ATTEMPTS} attempts — item left in specs/proposed ` +
|
|
1031
1031
|
'(no move). Try again shortly.';
|
|
1032
1032
|
note(message);
|
|
1033
1033
|
return {moved: false, reasonNotMoved: message};
|
|
@@ -1062,7 +1062,7 @@ export interface ListPromotableResult {
|
|
|
1062
1062
|
|
|
1063
1063
|
/**
|
|
1064
1064
|
* LIST every staged item awaiting a runner/human promotion — the tasks in
|
|
1065
|
-
* `work/pre-backlog/` and the prds in `work/
|
|
1065
|
+
* `work/pre-backlog/` and the prds in `work/specs/proposed/` on `<arbiter>/main` (the
|
|
1066
1066
|
* discovery half of the `promote` verb, so `promote` with no argument answers
|
|
1067
1067
|
* "what is staged waiting for me?"). It reads the ARBITER's truth (a fetch + a
|
|
1068
1068
|
* tree read), NOT the local working tree (which may be stale) — the same source
|
package/src/orphan-sidecar.ts
CHANGED
|
@@ -8,12 +8,12 @@ import {workFolderPath, workFolderRel} from './work-layout.js';
|
|
|
8
8
|
/**
|
|
9
9
|
* The **orphan-sidecar reaper** — the WORKING-TREE counterpart of the job-worktree
|
|
10
10
|
* and remote-branch reapers (`gc.ts` / `reap-branches.ts`), folded into `dorfl gc`
|
|
11
|
-
* (
|
|
11
|
+
* (spec `agentic-question-resolution-retire-disposition-vocabulary`, US #10).
|
|
12
12
|
*
|
|
13
13
|
* A question sidecar is a tooling-owned file `work/questions/<type>-<slug>.md`
|
|
14
14
|
* keyed on its source item's `(type, slug)` identity (`sidecarPathFor`). The
|
|
15
15
|
* contract is that notes/sidecars leave by DELETION (git history is the archive),
|
|
16
|
-
* so when a human deletes the source observation (or task/
|
|
16
|
+
* so when a human deletes the source observation (or task/spec) out-of-band, its
|
|
17
17
|
* sidecar is ORPHANED.
|
|
18
18
|
*
|
|
19
19
|
* WHY THIS IS A SWEEP, NOT AN APPLY STEP. An orphan's source item is GONE, so it
|
package/src/placement.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* **The shared STAGING/POOL placement resolver** (
|
|
2
|
+
* **The shared STAGING/POOL placement resolver** (spec
|
|
3
3
|
* `staging-pool-position-gate-and-trust-model`, task
|
|
4
4
|
* `runner-deterministic-slice-placement-policy-and-precedence`, governing ADR
|
|
5
5
|
* `placement-is-runner-deterministic-humanonly-is-agent-judgement`).
|
|
@@ -32,18 +32,18 @@
|
|
|
32
32
|
* onto the lifecycle's concrete folder name via {@link PlacementSlots}.
|
|
33
33
|
*
|
|
34
34
|
* - `'staging'` — land in the staging area (tasks: `pre-backlog/`; prds:
|
|
35
|
-
* `
|
|
35
|
+
* `specs/proposed/`). Not in the agent pool; a human/runner promotion is needed to make
|
|
36
36
|
* the item eligible. Review-without-PR review surface.
|
|
37
37
|
* - `'pool'` — land directly in the agent-eligible pool (tasks: `tasks/ready/`;
|
|
38
|
-
* prds: `
|
|
38
|
+
* prds: `specs/ready/`). The trusted-fast-path landing.
|
|
39
39
|
*/
|
|
40
40
|
export type PlacementSide = 'staging' | 'pool';
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* The two folder names a lifecycle uses for its staging/pool split. Supplied by
|
|
44
44
|
* the caller so this resolver stays lifecycle-generic (the TASK caller passes
|
|
45
|
-
* `{staging: 'pre-backlog', pool: 'tasks/ready'}`; the
|
|
46
|
-
* `{staging: '
|
|
45
|
+
* `{staging: 'pre-backlog', pool: 'tasks/ready'}`; the SPEC-placement caller passes
|
|
46
|
+
* `{staging: 'specs/proposed', pool: 'specs/ready'}`).
|
|
47
47
|
*/
|
|
48
48
|
export interface PlacementSlots {
|
|
49
49
|
staging: string;
|
|
@@ -100,7 +100,7 @@ const BUILT_IN_FLOOR: PlacementSide = 'staging';
|
|
|
100
100
|
* explicit > untrusted-origin ⇒ staging > configured default > built-in
|
|
101
101
|
*
|
|
102
102
|
* Pure: no I/O, no env reads — the caller resolves config + reads the
|
|
103
|
-
* frontmatter and passes both in. Reused by every lifecycle (task +
|
|
103
|
+
* frontmatter and passes both in. Reused by every lifecycle (task + spec
|
|
104
104
|
* placement + future intake variants) so a precedence change touches ONE place.
|
|
105
105
|
*/
|
|
106
106
|
export function resolvePlacement(
|
package/src/prd-to-spec.ts
CHANGED
|
@@ -276,6 +276,14 @@ export interface ResyncedDoc {
|
|
|
276
276
|
dest: string;
|
|
277
277
|
/** True when the target already had byte-identical content (a no-op copy). */
|
|
278
278
|
unchanged: boolean;
|
|
279
|
+
/**
|
|
280
|
+
* True when the doc's SOURCE could not be resolved (the package/dev copy is
|
|
281
|
+
* missing), so NOTHING was copied to `dest`. A skipped doc is NEVER counted as
|
|
282
|
+
* a change (it must not bump `VERSION` — the latent bug this field guards) and
|
|
283
|
+
* is surfaced LOUDLY by the caller. Distinct from `unchanged` (which means the
|
|
284
|
+
* source WAS resolved and matched the dest byte-for-byte).
|
|
285
|
+
*/
|
|
286
|
+
skipped: boolean;
|
|
279
287
|
}
|
|
280
288
|
|
|
281
289
|
/** What the protocol re-sync did (or would do). */
|
|
@@ -293,40 +301,68 @@ export interface ResyncResult {
|
|
|
293
301
|
* (`skills/setup/protocol/`) copy — NEVER the target repo's own (old) copy. This
|
|
294
302
|
* is how the migrated repo picks up the new `spec` contract before its data is
|
|
295
303
|
* converted. Idempotent: a doc already byte-identical is reported `unchanged`.
|
|
304
|
+
* A doc whose SOURCE cannot be resolved is reported `skipped` (NOT copied, NEVER
|
|
305
|
+
* a VERSION-bump) rather than silently bumping VERSION with nothing copied.
|
|
296
306
|
*
|
|
297
|
-
* `dryRun` reports what WOULD be copied without writing.
|
|
307
|
+
* `dryRun` reports what WOULD be copied without writing. `resolveDoc` overrides
|
|
308
|
+
* how a doc's source path is resolved (defaults to {@link resolveProtocolDoc});
|
|
309
|
+
* it exists so a test can force a non-resolvable source (a path that does not
|
|
310
|
+
* exist) to exercise the skip path.
|
|
298
311
|
*/
|
|
299
312
|
export function resyncProtocol(
|
|
300
313
|
repoPath: string,
|
|
301
|
-
options: {
|
|
314
|
+
options: {
|
|
315
|
+
dryRun?: boolean;
|
|
316
|
+
resolveDoc?: (name: string) => string;
|
|
317
|
+
} = {},
|
|
302
318
|
): ResyncResult {
|
|
303
319
|
const protocolDir = join(repoPath, WORK_ROOT, 'protocol');
|
|
320
|
+
const resolveDoc = options.resolveDoc ?? ((name) => resolveProtocolDoc(name));
|
|
304
321
|
const docs: ResyncedDoc[] = [];
|
|
305
322
|
for (const name of PROTOCOL_DOCS) {
|
|
306
323
|
// Resolve the SOURCE (package/dev), never the target's adopted copy.
|
|
307
|
-
const source =
|
|
324
|
+
const source = resolveDoc(name);
|
|
308
325
|
const destAbs = join(protocolDir, name);
|
|
309
326
|
const destRel = relative(repoPath, destAbs);
|
|
310
|
-
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
|
|
327
|
+
const sourceExists = existsSync(source);
|
|
328
|
+
if (!sourceExists) {
|
|
329
|
+
// The source doc could not be resolved: copy NOTHING and record the doc as
|
|
330
|
+
// SKIPPED (never a change). This is the latent-bug fix: a non-resolvable
|
|
331
|
+
// source must not count as `changed` and bump `VERSION` while copying no
|
|
332
|
+
// file. `unchanged: false` here is honest (the dest was not made to match a
|
|
333
|
+
// source), but `skipped: true` keeps it OUT of the VERSION-bump tally.
|
|
334
|
+
docs.push({name, dest: destRel, unchanged: false, skipped: true});
|
|
335
|
+
continue;
|
|
314
336
|
}
|
|
315
|
-
|
|
337
|
+
// Source resolved: a doc is CHANGED (VERSION-bump-worthy) only when the dest
|
|
338
|
+
// is absent OR its content differs from the source. An identical dest is a
|
|
339
|
+
// no-op copy (`unchanged`), so an already-synced re-run stays a true no-op.
|
|
340
|
+
const unchanged =
|
|
341
|
+
existsSync(destAbs) &&
|
|
342
|
+
readFileSync(source, 'utf8') === readFileSync(destAbs, 'utf8');
|
|
343
|
+
if (!options.dryRun) {
|
|
316
344
|
mkdirSync(dirname(destAbs), {recursive: true});
|
|
317
345
|
copyFileSync(source, destAbs);
|
|
318
346
|
}
|
|
319
|
-
docs.push({name, dest: destRel, unchanged});
|
|
347
|
+
docs.push({name, dest: destRel, unchanged, skipped: false});
|
|
320
348
|
}
|
|
321
349
|
|
|
322
350
|
const versionAbs = join(protocolDir, 'VERSION');
|
|
323
351
|
const versionRel = relative(repoPath, versionAbs);
|
|
324
|
-
// Bump VERSION only when a doc
|
|
325
|
-
//
|
|
326
|
-
// (
|
|
327
|
-
//
|
|
328
|
-
|
|
329
|
-
|
|
352
|
+
// Bump VERSION only when a doc was ACTUALLY COPIED with new content, or when
|
|
353
|
+
// VERSION is absent AND at least one doc was genuinely synced (copied). A
|
|
354
|
+
// SKIPPED doc (unresolvable source, nothing copied) is NEVER a change —
|
|
355
|
+
// counting it would bump VERSION without copying a single doc (the latent bug),
|
|
356
|
+
// and the write-when-absent fallback must ALSO require a real copy so an
|
|
357
|
+
// all-skipped resync leaves no VERSION behind. A re-run on an already-synced
|
|
358
|
+
// repo must stay a true no-op — otherwise the (always-fresh) `synced-at`
|
|
359
|
+
// timestamp would dirty the tree on every idempotent re-run.
|
|
360
|
+
const anyDocChanged = docs.some((d) => !d.unchanged && !d.skipped);
|
|
361
|
+
const anyDocSynced = docs.some((d) => !d.skipped);
|
|
362
|
+
if (
|
|
363
|
+
!options.dryRun &&
|
|
364
|
+
(anyDocChanged || (anyDocSynced && !existsSync(versionAbs)))
|
|
365
|
+
) {
|
|
330
366
|
mkdirSync(dirname(versionAbs), {recursive: true});
|
|
331
367
|
const today = new Date().toISOString().slice(0, 10);
|
|
332
368
|
writeFileSync(
|
package/src/prompt.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Builds the prompt the runner hands to `agentCmd`: a small CONSTANT wrapper
|
|
3
|
-
* (only the `<slug>` / source-
|
|
3
|
+
* (only the `<slug>` / source-spec path vary) around the claimed task's own
|
|
4
4
|
* `## Prompt` section. This is dual-use — the SAME assembly the autonomous
|
|
5
5
|
* runner feeds `agentCmd` and the human `dorfl prompt [<slug>]` command.
|
|
6
6
|
*
|
|
7
7
|
* The wrapper is NOT hardcoded here: it is read VERBATIM from the work-contract
|
|
8
8
|
* (`skills/setup/protocol/CLAIM-PROTOCOL.md` → "The prompt handed to the work agent"),
|
|
9
9
|
* so the emitted text can never silently diverge from the canonical contract.
|
|
10
|
-
* We only substitute the per-task placeholders (`<slug>`, `<
|
|
10
|
+
* We only substitute the per-task placeholders (`<slug>`, `<spec>`).
|
|
11
11
|
*
|
|
12
12
|
* The wrapper draws the git boundary IN-BAND — the spawned agent does NO git ops
|
|
13
13
|
* on the repo (no commit/push, no moving `work/` files); the RUNNER owns every
|
|
@@ -137,7 +137,7 @@ export function resolveProtocolDoc(
|
|
|
137
137
|
/**
|
|
138
138
|
* Pull the canonical wrapper TEMPLATE out of CLAIM-PROTOCOL.md: the first fenced
|
|
139
139
|
* code block following the "The prompt handed to the work agent" heading. The
|
|
140
|
-
* returned text still contains the `<slug>` / `<
|
|
140
|
+
* returned text still contains the `<slug>` / `<spec>` placeholders verbatim — it
|
|
141
141
|
* is the single source of truth for the wrapper.
|
|
142
142
|
*/
|
|
143
143
|
export function extractCanonicalWrapperTemplate(protocol: string): string {
|
|
@@ -208,7 +208,7 @@ export function extractCanonicalWrapperTemplate(protocol: string): string {
|
|
|
208
208
|
*
|
|
209
209
|
* Pure string transform; runs AFTER `extractCanonicalWrapperTemplate` (a pure
|
|
210
210
|
* verbatim extractor stays a pure verbatim extractor) and BEFORE the
|
|
211
|
-
* `<slug>`/`<
|
|
211
|
+
* `<slug>`/`<spec>` substitution.
|
|
212
212
|
*/
|
|
213
213
|
export function applyPromptGuidance(
|
|
214
214
|
template: string,
|
|
@@ -227,17 +227,17 @@ export function applyPromptGuidance(
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
/**
|
|
230
|
-
* The constant wrapper, parameterised only by the task slug, its source
|
|
230
|
+
* The constant wrapper, parameterised only by the task slug, its source spec
|
|
231
231
|
* slug, and (optionally) the resolved {@link PromptGuidance} nudges. Read
|
|
232
232
|
* verbatim from the work-contract and substituted — never a divergent hardcoded
|
|
233
|
-
* copy. `
|
|
233
|
+
* copy. `spec` may be `undefined` when the task has no `spec:` field. When
|
|
234
234
|
* `promptGuidance` is omitted (or every member resolves false) the output is
|
|
235
235
|
* BYTE-IDENTICAL to today's wrapper (the ELSE-branch of every conditional is
|
|
236
236
|
* the historic text).
|
|
237
237
|
*/
|
|
238
238
|
export function wrapper(
|
|
239
239
|
slug: string,
|
|
240
|
-
|
|
240
|
+
spec: string | undefined,
|
|
241
241
|
options: {
|
|
242
242
|
protocolPath?: string;
|
|
243
243
|
cwd?: string;
|
|
@@ -252,7 +252,7 @@ export function wrapper(
|
|
|
252
252
|
const protocol = readFileSync(protocolPath, 'utf8');
|
|
253
253
|
const template = extractCanonicalWrapperTemplate(protocol);
|
|
254
254
|
const resolved = applyPromptGuidance(template, options.promptGuidance);
|
|
255
|
-
return resolved.replace(/<slug>/g, slug).replace(/<
|
|
255
|
+
return resolved.replace(/<slug>/g, slug).replace(/<spec>/g, spec ?? '<spec>');
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
/**
|
|
@@ -416,7 +416,7 @@ export function buildContinueBlock(slug: string, ctx: ContinueContext): string {
|
|
|
416
416
|
}
|
|
417
417
|
|
|
418
418
|
/**
|
|
419
|
-
* Build the full prompt: the canonical wrapper for `slug` (with its source
|
|
419
|
+
* Build the full prompt: the canonical wrapper for `slug` (with its source spec
|
|
420
420
|
* substituted) followed by the task's own `## Prompt` body, appended verbatim.
|
|
421
421
|
*
|
|
422
422
|
* In CONTINUE-mode (a {@link ContinueContext} is supplied), a CONTINUE block is
|
|
@@ -427,7 +427,7 @@ export function buildContinueBlock(slug: string, ctx: ContinueContext): string {
|
|
|
427
427
|
*/
|
|
428
428
|
export function buildAgentPrompt(
|
|
429
429
|
slug: string,
|
|
430
|
-
|
|
430
|
+
spec: string | undefined,
|
|
431
431
|
taskPrompt: string,
|
|
432
432
|
options: {
|
|
433
433
|
protocolPath?: string;
|
|
@@ -436,7 +436,7 @@ export function buildAgentPrompt(
|
|
|
436
436
|
promptGuidance?: {testFirst?: boolean};
|
|
437
437
|
} = {},
|
|
438
438
|
): string {
|
|
439
|
-
const head = wrapper(slug,
|
|
439
|
+
const head = wrapper(slug, spec, options);
|
|
440
440
|
if (options.continueContext) {
|
|
441
441
|
const block = buildContinueBlock(slug, options.continueContext);
|
|
442
442
|
return `${head}\n\n${block}\n\n${taskPrompt}\n`;
|
|
@@ -459,8 +459,8 @@ export interface ResolvedTask {
|
|
|
459
459
|
path: string;
|
|
460
460
|
/** The folder the task was resolved from (in-progress wins over tasks-ready). */
|
|
461
461
|
folder: TaskFolder;
|
|
462
|
-
/** The task's source
|
|
463
|
-
|
|
462
|
+
/** The task's source SPEC slug (frontmatter `spec:`), if any. */
|
|
463
|
+
spec: string | undefined;
|
|
464
464
|
/** The extracted `## Prompt` body. */
|
|
465
465
|
taskPrompt: string;
|
|
466
466
|
}
|
|
@@ -490,7 +490,7 @@ export class PromptError extends Error {}
|
|
|
490
490
|
/**
|
|
491
491
|
* The CONTINUE-only gate that lets {@link resolveTask} reach a task that has
|
|
492
492
|
* already been done-moved into `work/done/` — story 5 of the `ledger-integrity`
|
|
493
|
-
*
|
|
493
|
+
* spec (defect 3). A continue/re-claim can legitimately land on a branch whose
|
|
494
494
|
* task was ALREADY moved to `done/` (the green-but-unpushed STRAND state), and
|
|
495
495
|
* onboard must find it; but a `done/` task is folder-indistinguishable between
|
|
496
496
|
* two states and re-onboarding a genuinely-finished one would RE-RUN it. So
|
|
@@ -530,7 +530,7 @@ export interface ContinueResolutionGate {
|
|
|
530
530
|
|
|
531
531
|
/**
|
|
532
532
|
* Extra knobs for {@link resolveTask} beyond the continue gate. Today the only
|
|
533
|
-
* member is `allowBacklog` (
|
|
533
|
+
* member is `allowBacklog` (spec
|
|
534
534
|
* `do-allow-backlog-drive-staged-tasks-without-promotion`): the operator's
|
|
535
535
|
* EXPLICIT `do … --allow-backlog`, which widens resolution to also search
|
|
536
536
|
* `tasks-backlog` (staging) at LOWEST priority. Default off (omitted) \u21d2 the
|
|
@@ -571,7 +571,7 @@ function isStrandedDoneTip(gate: ContinueResolutionGate): boolean {
|
|
|
571
571
|
|
|
572
572
|
/**
|
|
573
573
|
* Resolve a task's file: prefer `work/in-progress/<slug>.md`, fall back to
|
|
574
|
-
* `work/tasks/ready/<slug>.md` (the pool). Returns the parsed
|
|
574
|
+
* `work/tasks/ready/<slug>.md` (the pool). Returns the parsed spec + extracted
|
|
575
575
|
* `## Prompt` body. Throws {@link PromptError} when neither file exists or it has
|
|
576
576
|
* no prompt body.
|
|
577
577
|
*
|
|
@@ -585,11 +585,11 @@ function isStrandedDoneTip(gate: ContinueResolutionGate): boolean {
|
|
|
585
585
|
* the only addition, and it is gated. With no gate (a fresh claim) the behaviour
|
|
586
586
|
* is byte-identical to the original `['in-progress','tasks-ready']`-only resolution.
|
|
587
587
|
*
|
|
588
|
-
* `--allow-backlog` (
|
|
588
|
+
* `--allow-backlog` (spec `do-allow-backlog-drive-staged-tasks-without-promotion`):
|
|
589
589
|
* when {@link ResolveTaskOptions.allowBacklog} is set, `tasks-backlog` (the staging
|
|
590
590
|
* folder) is appended at the LOWEST priority — after `tasks-ready`, so a slug
|
|
591
591
|
* present in BOTH the pool and staging resolves to the READY copy (the same-slug
|
|
592
|
-
* precedence the
|
|
592
|
+
* precedence the spec's decision 5 fixes). Default off ⇒ byte-identical to today
|
|
593
593
|
* (no autonomous path sets it; staging stays unreachable to `run`/auto-pick).
|
|
594
594
|
*/
|
|
595
595
|
export function resolveTask(
|
|
@@ -624,11 +624,10 @@ export function resolveTask(
|
|
|
624
624
|
);
|
|
625
625
|
}
|
|
626
626
|
const fm = parseFrontmatter(content);
|
|
627
|
-
//
|
|
628
|
-
//
|
|
629
|
-
// `
|
|
630
|
-
|
|
631
|
-
return {slug, path, folder, prd: fm.spec, taskPrompt};
|
|
627
|
+
// Read the parent-spec pointer off `fm.spec` (populated from the canonical
|
|
628
|
+
// `spec:` key) into the `spec`-only `ResolvedTask` (spec
|
|
629
|
+
// `prd-to-spec-vocabulary-cutover-and-migration-command`).
|
|
630
|
+
return {slug, path, folder, spec: fm.spec, taskPrompt};
|
|
632
631
|
}
|
|
633
632
|
const searched = order.map((f) => `${workFolderRel(f)}/`).join(', ');
|
|
634
633
|
throw new PromptError(`no task '${slug}' found in ${searched}`);
|
|
@@ -662,13 +661,13 @@ export function inferSlugFromBranch(
|
|
|
662
661
|
*/
|
|
663
662
|
/**
|
|
664
663
|
* Resolve the EFFECTIVE `promptGuidance` for an item by walking the precedence
|
|
665
|
-
* chain (highest → lowest): the per-task frontmatter override, the per-
|
|
666
|
-
* frontmatter override (only when the task carries a `
|
|
664
|
+
* chain (highest → lowest): the per-task frontmatter override, the per-spec
|
|
665
|
+
* frontmatter override (only when the task carries a `spec:`), then the
|
|
667
666
|
* already-resolved repo policy. Each nudge member resolves independently — a
|
|
668
667
|
* task's `promptGuidance.testFirst` override never bleeds into a sibling
|
|
669
668
|
* member — mirroring the `humanOnly`/`autoBuild` per-item override shape.
|
|
670
669
|
*
|
|
671
|
-
* A task may carry the override even when it has NO `
|
|
670
|
+
* A task may carry the override even when it has NO `spec:` (a self-contained
|
|
672
671
|
* chore), by symmetry with `humanOnly` at the item level; `specFrontmatter`
|
|
673
672
|
* is then simply absent and the chain reads task ⇒ repo.
|
|
674
673
|
*/
|
|
@@ -685,11 +684,11 @@ export function resolveItemPromptGuidance(
|
|
|
685
684
|
}
|
|
686
685
|
|
|
687
686
|
/**
|
|
688
|
-
* Locate a
|
|
689
|
-
* auto-slice pool), then fall back to `work/
|
|
687
|
+
* Locate a spec's file on disk: prefer `work/specs/ready/<slug>.md` (the
|
|
688
|
+
* auto-slice pool), then fall back to `work/specs/tasked/<slug>.md` (tasked,
|
|
690
689
|
* resting). Returns `undefined` when neither exists — the caller treats that
|
|
691
|
-
* as "no
|
|
692
|
-
* through to the repo policy (a missing
|
|
690
|
+
* as "no spec-level override available" and the precedence chain falls
|
|
691
|
+
* through to the repo policy (a missing spec is NOT an error at this seam;
|
|
693
692
|
* the per-item override is OPTIONAL by design).
|
|
694
693
|
*/
|
|
695
694
|
export function findSpecPath(
|
|
@@ -711,7 +710,7 @@ export function findSpecPath(
|
|
|
711
710
|
/**
|
|
712
711
|
* The convenience seam every caller of {@link buildAgentPrompt} reuses to
|
|
713
712
|
* resolve the per-item override: load the task frontmatter from its file +
|
|
714
|
-
* (when the task carries `
|
|
713
|
+
* (when the task carries `spec:`) the spec frontmatter, then walk
|
|
715
714
|
* {@link resolveItemPromptGuidance}. Pure-ish (reads at most two files);
|
|
716
715
|
* returns the repo policy verbatim when neither item layer overrides anything.
|
|
717
716
|
*/
|
|
@@ -722,8 +721,7 @@ export function resolvePromptGuidanceForItem(options: {
|
|
|
722
721
|
}): PromptGuidance {
|
|
723
722
|
const taskFm = parseFrontmatter(options.taskContent);
|
|
724
723
|
let specFm: Frontmatter | undefined;
|
|
725
|
-
//
|
|
726
|
-
// read the parent-spec pointer off `fm.spec` (populated beside `fm.prd`).
|
|
724
|
+
// Read the parent-spec pointer off `fm.spec` (populated from the `spec:` key).
|
|
727
725
|
if (taskFm.spec !== undefined) {
|
|
728
726
|
const specPath = findSpecPath(options.cwd, taskFm.spec);
|
|
729
727
|
if (specPath !== undefined) {
|
|
@@ -742,7 +740,7 @@ export function renderPrompt(options: PromptOptions): string {
|
|
|
742
740
|
);
|
|
743
741
|
}
|
|
744
742
|
const task = resolveTask(options.cwd, slug);
|
|
745
|
-
// Per-item override layer: a task or
|
|
743
|
+
// Per-item override layer: a task or spec may pin `promptGuidance.testFirst`
|
|
746
744
|
// in its frontmatter, superseding the resolved repo policy for THIS item.
|
|
747
745
|
// We ALWAYS walk the resolver (even when no repo policy was threaded), so a
|
|
748
746
|
// task can opt IN to the strengthened nudge even on a repo whose default is
|
|
@@ -752,7 +750,7 @@ export function renderPrompt(options: PromptOptions): string {
|
|
|
752
750
|
repoResolved: {testFirst: options.promptGuidance?.testFirst === true},
|
|
753
751
|
taskContent: readFileSync(task.path, 'utf8'),
|
|
754
752
|
});
|
|
755
|
-
return buildAgentPrompt(task.slug, task.
|
|
753
|
+
return buildAgentPrompt(task.slug, task.spec, task.taskPrompt, {
|
|
756
754
|
protocolPath: options.protocolPath,
|
|
757
755
|
cwd: options.cwd,
|
|
758
756
|
promptGuidance: resolvedGuidance,
|
package/src/readiness.ts
CHANGED
|
@@ -16,7 +16,7 @@ import {ledgerRead} from './ledger-read.js';
|
|
|
16
16
|
* the shared {@link resolveBlockedBy} (no reimplemented dep resolution).
|
|
17
17
|
*
|
|
18
18
|
* The two axes are deliberately treated differently (see WORK-CONTRACT and the
|
|
19
|
-
* task
|
|
19
|
+
* task spec):
|
|
20
20
|
*
|
|
21
21
|
* - `blockedBy` unmet is a FACTUAL prerequisite (the dep work does not exist
|
|
22
22
|
* yet) → REFUSE by default. An override flag is the human escape hatch.
|
package/src/recover-isolated.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type {ReviewProvider} from './integrator.js';
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* **`complete --isolated <slug>` — FINISH an already-committed, already-done-moved
|
|
14
|
-
* STRANDED isolated worktree** (
|
|
14
|
+
* STRANDED isolated worktree** (spec `ledger-integrity` story 6, the
|
|
15
15
|
* `finish-already-committed-branch` task). The LOCATE-EXISTING inverse of
|
|
16
16
|
* `do --isolated` (which CREATES a worktree): it resolves the slug's RETAINED job
|
|
17
17
|
* worktree via the existing arbiter-URL-keyed naming
|