dorfl 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advance-drivers.d.ts +1 -1
- package/dist/advance-drivers.d.ts.map +1 -1
- package/dist/advance-drivers.js +15 -3
- package/dist/advance-drivers.js.map +1 -1
- package/dist/advance-lifecycle-template.d.ts.map +1 -1
- package/dist/advance-lifecycle-template.js +76 -4
- package/dist/advance-lifecycle-template.js.map +1 -1
- package/dist/advance-treeless-publish.d.ts +23 -0
- package/dist/advance-treeless-publish.d.ts.map +1 -1
- package/dist/advance-treeless-publish.js +41 -0
- package/dist/advance-treeless-publish.js.map +1 -1
- package/dist/advance.d.ts +59 -7
- package/dist/advance.d.ts.map +1 -1
- package/dist/advance.js +389 -82
- package/dist/advance.js.map +1 -1
- package/dist/advancing-lock.d.ts +30 -2
- package/dist/advancing-lock.d.ts.map +1 -1
- package/dist/advancing-lock.js +50 -0
- package/dist/advancing-lock.js.map +1 -1
- package/dist/agent-launch.d.ts +12 -0
- package/dist/agent-launch.d.ts.map +1 -1
- package/dist/agent-launch.js +22 -12
- package/dist/agent-launch.js.map +1 -1
- package/dist/agent-stop.d.ts +40 -2
- package/dist/agent-stop.d.ts.map +1 -1
- package/dist/agent-stop.js +30 -2
- package/dist/agent-stop.js.map +1 -1
- package/dist/apply-decide.d.ts +17 -3
- package/dist/apply-decide.d.ts.map +1 -1
- package/dist/apply-decide.js +36 -7
- package/dist/apply-decide.js.map +1 -1
- package/dist/apply-merge-action.d.ts +17 -5
- package/dist/apply-merge-action.d.ts.map +1 -1
- package/dist/apply-merge-action.js +43 -8
- package/dist/apply-merge-action.js.map +1 -1
- package/dist/apply-persist.d.ts +57 -26
- package/dist/apply-persist.d.ts.map +1 -1
- package/dist/apply-persist.js +166 -37
- package/dist/apply-persist.js.map +1 -1
- package/dist/apply-stuck-action.d.ts +151 -0
- package/dist/apply-stuck-action.d.ts.map +1 -0
- package/dist/apply-stuck-action.js +125 -0
- package/dist/apply-stuck-action.js.map +1 -0
- package/dist/brand.d.ts +12 -1
- package/dist/brand.d.ts.map +1 -1
- package/dist/brand.js +2 -1
- package/dist/brand.js.map +1 -1
- package/dist/cli-spinner.d.ts +1 -1
- package/dist/cli-spinner.js +1 -1
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +320 -63
- package/dist/cli.js.map +1 -1
- package/dist/close-job-template.js +1 -1
- package/dist/complete.d.ts +6 -3
- package/dist/complete.d.ts.map +1 -1
- package/dist/complete.js +91 -37
- package/dist/complete.js.map +1 -1
- package/dist/config.d.ts +55 -9
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +56 -4
- package/dist/config.js.map +1 -1
- package/dist/continue-branch.d.ts.map +1 -1
- package/dist/continue-branch.js +22 -0
- package/dist/continue-branch.js.map +1 -1
- package/dist/decision-engine.d.ts +38 -10
- package/dist/decision-engine.d.ts.map +1 -1
- package/dist/decision-engine.js +10 -6
- package/dist/decision-engine.js.map +1 -1
- package/dist/do-autopick.d.ts.map +1 -1
- package/dist/do-autopick.js +11 -1
- package/dist/do-autopick.js.map +1 -1
- package/dist/do-config.d.ts +1 -1
- package/dist/do.d.ts +49 -56
- package/dist/do.d.ts.map +1 -1
- package/dist/do.js +419 -30
- package/dist/do.js.map +1 -1
- package/dist/env-config.d.ts.map +1 -1
- package/dist/env-config.js +12 -4
- package/dist/env-config.js.map +1 -1
- package/dist/failure-cause.d.ts +3 -2
- package/dist/failure-cause.d.ts.map +1 -1
- package/dist/failure-cause.js +28 -1
- package/dist/failure-cause.js.map +1 -1
- package/dist/format.d.ts +6 -6
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +10 -27
- package/dist/format.js.map +1 -1
- package/dist/frontmatter.d.ts +16 -4
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js +27 -1
- package/dist/frontmatter.js.map +1 -1
- package/dist/gc.d.ts +65 -6
- package/dist/gc.d.ts.map +1 -1
- package/dist/gc.js +126 -9
- package/dist/gc.js.map +1 -1
- package/dist/github.d.ts +14 -0
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +73 -0
- package/dist/github.js.map +1 -1
- package/dist/harness.d.ts +25 -0
- package/dist/harness.d.ts.map +1 -1
- package/dist/harness.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/install-ci-branch-protection.d.ts +132 -38
- package/dist/install-ci-branch-protection.d.ts.map +1 -1
- package/dist/install-ci-branch-protection.js +190 -48
- package/dist/install-ci-branch-protection.js.map +1 -1
- package/dist/install-ci-core.d.ts +21 -1
- package/dist/install-ci-core.d.ts.map +1 -1
- package/dist/install-ci-core.js +10 -3
- package/dist/install-ci-core.js.map +1 -1
- package/dist/install-ci-github.d.ts +32 -0
- package/dist/install-ci-github.d.ts.map +1 -1
- package/dist/install-ci-github.js +73 -0
- package/dist/install-ci-github.js.map +1 -1
- package/dist/install-skills.d.ts +123 -0
- package/dist/install-skills.d.ts.map +1 -0
- package/dist/install-skills.js +100 -0
- package/dist/install-skills.js.map +1 -0
- package/dist/intake-trigger-template.js +1 -1
- package/dist/intake.d.ts +17 -4
- package/dist/intake.d.ts.map +1 -1
- package/dist/intake.js +23 -7
- package/dist/intake.js.map +1 -1
- package/dist/integration-core.d.ts +10 -7
- package/dist/integration-core.d.ts.map +1 -1
- package/dist/integration-core.js +174 -60
- package/dist/integration-core.js.map +1 -1
- package/dist/integrator.d.ts +1 -1
- package/dist/integrator.d.ts.map +1 -1
- package/dist/integrator.js +19 -3
- package/dist/integrator.js.map +1 -1
- package/dist/isolation.d.ts +2 -2
- package/dist/isolation.d.ts.map +1 -1
- package/dist/isolation.js +9 -0
- package/dist/isolation.js.map +1 -1
- package/dist/item-lock.d.ts +196 -129
- package/dist/item-lock.d.ts.map +1 -1
- package/dist/item-lock.js +329 -254
- package/dist/item-lock.js.map +1 -1
- package/dist/ledger-write.d.ts +31 -26
- package/dist/ledger-write.d.ts.map +1 -1
- package/dist/ledger-write.js +100 -122
- package/dist/ledger-write.js.map +1 -1
- package/dist/lifecycle-gather.d.ts +17 -0
- package/dist/lifecycle-gather.d.ts.map +1 -1
- package/dist/lifecycle-gather.js +7 -1
- package/dist/lifecycle-gather.js.map +1 -1
- package/dist/lifecycle-pools.d.ts +40 -3
- package/dist/lifecycle-pools.d.ts.map +1 -1
- package/dist/lifecycle-pools.js +26 -6
- package/dist/lifecycle-pools.js.map +1 -1
- package/dist/merge-question-surfacer.d.ts +15 -1
- package/dist/merge-question-surfacer.d.ts.map +1 -1
- package/dist/merge-question-surfacer.js +18 -3
- package/dist/merge-question-surfacer.js.map +1 -1
- package/dist/migrate-stuck-locks.d.ts +129 -0
- package/dist/migrate-stuck-locks.d.ts.map +1 -0
- package/dist/migrate-stuck-locks.js +355 -0
- package/dist/migrate-stuck-locks.js.map +1 -0
- package/dist/needs-attention.d.ts +293 -16
- package/dist/needs-attention.d.ts.map +1 -1
- package/dist/needs-attention.js +532 -57
- package/dist/needs-attention.js.map +1 -1
- package/dist/orphan-sidecar.d.ts +8 -4
- package/dist/orphan-sidecar.d.ts.map +1 -1
- package/dist/orphan-sidecar.js +35 -2
- package/dist/orphan-sidecar.js.map +1 -1
- package/dist/pi-harness.d.ts +16 -0
- package/dist/pi-harness.d.ts.map +1 -1
- package/dist/pi-harness.js +82 -2
- package/dist/pi-harness.js.map +1 -1
- package/dist/placement.d.ts +3 -3
- package/dist/placement.js +1 -1
- package/dist/prd-to-spec.d.ts.map +1 -1
- package/dist/prd-to-spec.js +9 -5
- package/dist/prd-to-spec.js.map +1 -1
- package/dist/protocol/CLAIM-PROTOCOL.md +17 -10
- package/dist/protocol/REVIEW-PROTOCOL.md +4 -1
- package/dist/protocol/SURFACE-PROTOCOL.md +16 -2
- package/dist/protocol/TASKING-PROTOCOL.md +3 -1
- package/dist/protocol/WORK-CONTRACT.md +22 -18
- package/dist/protocol/task-template.md +1 -1
- package/dist/reap-branches.d.ts +12 -9
- package/dist/reap-branches.d.ts.map +1 -1
- package/dist/reap-branches.js +25 -7
- package/dist/reap-branches.js.map +1 -1
- package/dist/recover-isolated.d.ts +12 -0
- package/dist/recover-isolated.d.ts.map +1 -1
- package/dist/recover-isolated.js +5 -0
- package/dist/recover-isolated.js.map +1 -1
- package/dist/repo-config.d.ts +23 -2
- package/dist/repo-config.d.ts.map +1 -1
- package/dist/repo-config.js +53 -7
- package/dist/repo-config.js.map +1 -1
- package/dist/repo-mirror.d.ts.map +1 -1
- package/dist/repo-mirror.js +18 -2
- package/dist/repo-mirror.js.map +1 -1
- package/dist/review-verdict.js +1 -1
- package/dist/review-verdict.js.map +1 -1
- package/dist/run.d.ts +1 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +54 -19
- package/dist/run.js.map +1 -1
- package/dist/scan.d.ts +17 -8
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +22 -8
- package/dist/scan.js.map +1 -1
- package/dist/select-priority.d.ts +2 -2
- package/dist/select-priority.js +1 -1
- package/dist/select-priority.js.map +1 -1
- package/dist/sidecar-apply.js +1 -1
- package/dist/sidecar-apply.js.map +1 -1
- package/dist/sidecar.d.ts +45 -10
- package/dist/sidecar.d.ts.map +1 -1
- package/dist/sidecar.js +82 -2
- package/dist/sidecar.js.map +1 -1
- package/dist/skills/answer-questions/SKILL.md +89 -0
- package/dist/skills/capture-signal/SKILL.md +52 -0
- package/dist/skills/convert-from-prd-to-spec/SKILL.md +90 -0
- package/dist/skills/drive-tasks/SKILL.md +218 -0
- package/dist/skills/from-idea/SKILL.md +83 -0
- package/dist/skills/merge-prs/SKILL.md +70 -0
- package/dist/skills/orchestrate/SKILL.md +101 -0
- package/dist/skills/promote/SKILL.md +35 -0
- package/dist/skills/review/SKILL.md +16 -0
- package/dist/skills/setup/SKILL.md +258 -0
- package/dist/skills/setup/protocol/ADR-FORMAT.md +47 -0
- package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +224 -0
- package/dist/skills/setup/protocol/REVIEW-PROTOCOL.md +122 -0
- package/dist/skills/setup/protocol/SURFACE-PROTOCOL.md +135 -0
- package/dist/skills/setup/protocol/TASKING-PROTOCOL.md +124 -0
- package/dist/skills/setup/protocol/WORK-CONTRACT.md +280 -0
- package/dist/skills/setup/protocol/spec-template.md +71 -0
- package/dist/skills/setup/protocol/task-template.md +65 -0
- package/dist/skills/surface-questions/SKILL.md +16 -0
- package/dist/skills/to-spec/SKILL.md +34 -0
- package/dist/skills/to-task/SKILL.md +19 -0
- package/dist/skills/triage-observations/SKILL.md +78 -0
- package/dist/skills/work/SKILL.md +51 -0
- package/dist/slug-namespace.d.ts +5 -5
- package/dist/slug-namespace.js +5 -5
- package/dist/slug-namespace.js.map +1 -1
- package/dist/spec-complete.d.ts.map +1 -1
- package/dist/spec-complete.js +2 -3
- package/dist/spec-complete.js.map +1 -1
- package/dist/start.d.ts.map +1 -1
- package/dist/start.js +51 -57
- package/dist/start.js.map +1 -1
- package/dist/surface-gate.d.ts.map +1 -1
- package/dist/surface-gate.js +10 -3
- package/dist/surface-gate.js.map +1 -1
- package/dist/surface-persist.js +1 -1
- package/dist/surface-persist.js.map +1 -1
- package/dist/tasker-review-loop.d.ts +4 -3
- package/dist/tasker-review-loop.d.ts.map +1 -1
- package/dist/tasker-review-loop.js.map +1 -1
- package/dist/tasking-lock.d.ts +2 -2
- package/dist/tasking-lock.d.ts.map +1 -1
- package/dist/tasking-lock.js +53 -32
- package/dist/tasking-lock.js.map +1 -1
- package/dist/tasking.d.ts +40 -6
- package/dist/tasking.d.ts.map +1 -1
- package/dist/tasking.js +224 -12
- package/dist/tasking.js.map +1 -1
- package/dist/triage-persist.d.ts +10 -6
- package/dist/triage-persist.d.ts.map +1 -1
- package/dist/triage-persist.js +34 -4
- package/dist/triage-persist.js.map +1 -1
- package/dist/vendor/incur/agents.d.ts +58 -0
- package/dist/vendor/incur/agents.d.ts.map +1 -0
- package/dist/vendor/incur/agents.js +343 -0
- package/dist/vendor/incur/agents.js.map +1 -0
- package/dist/verify-workflow-template.js +1 -1
- package/dist/watch-session.d.ts +11 -3
- package/dist/watch-session.d.ts.map +1 -1
- package/dist/watch-session.js +94 -7
- package/dist/watch-session.js.map +1 -1
- package/dist/work-layout.d.ts +7 -3
- package/dist/work-layout.d.ts.map +1 -1
- package/dist/work-layout.js +6 -3
- package/dist/work-layout.js.map +1 -1
- package/dist/workspace.d.ts +1 -1
- package/package.json +2 -2
- package/src/advance-drivers.ts +16 -3
- package/src/advance-lifecycle-template.ts +89 -4
- package/src/advance-treeless-publish.ts +45 -0
- package/src/advance.ts +467 -87
- package/src/advancing-lock.ts +96 -1
- package/src/agent-launch.ts +37 -12
- package/src/agent-stop.ts +60 -2
- package/src/apply-decide.ts +36 -7
- package/src/apply-merge-action.ts +45 -7
- package/src/apply-persist.ts +228 -54
- package/src/apply-stuck-action.ts +260 -0
- package/src/brand.ts +14 -2
- package/src/cli-spinner.ts +1 -1
- package/src/cli.ts +401 -70
- package/src/close-job-template.ts +1 -1
- package/src/complete.ts +110 -52
- package/src/config.ts +107 -13
- package/src/continue-branch.ts +22 -0
- package/src/decision-engine.ts +55 -17
- package/src/do-autopick.ts +12 -1
- package/src/do-config.ts +1 -1
- package/src/do.ts +511 -38
- package/src/env-config.ts +12 -4
- package/src/failure-cause.ts +30 -1
- package/src/format.ts +10 -30
- package/src/frontmatter.ts +35 -5
- package/src/gc.ts +172 -9
- package/src/github.ts +78 -0
- package/src/harness.ts +25 -0
- package/src/index.ts +10 -0
- package/src/install-ci-branch-protection.ts +282 -57
- package/src/install-ci-core.ts +30 -3
- package/src/install-ci-github.ts +86 -0
- package/src/install-skills.ts +166 -0
- package/src/intake-trigger-template.ts +1 -1
- package/src/intake.ts +46 -11
- package/src/integration-core.ts +199 -65
- package/src/integrator.ts +22 -9
- package/src/isolation.ts +11 -2
- package/src/item-lock.ts +485 -341
- package/src/ledger-write.ts +135 -150
- package/src/lifecycle-gather.ts +29 -6
- package/src/lifecycle-pools.ts +67 -9
- package/src/merge-question-surfacer.ts +28 -4
- package/src/migrate-stuck-locks.ts +451 -0
- package/src/needs-attention.ts +857 -84
- package/src/orphan-sidecar.ts +47 -6
- package/src/pi-harness.ts +82 -2
- package/src/placement.ts +3 -3
- package/src/prd-to-spec.ts +13 -5
- package/src/reap-branches.ts +38 -14
- package/src/recover-isolated.ts +17 -0
- package/src/repo-config.ts +55 -6
- package/src/repo-mirror.ts +23 -1
- package/src/review-verdict.ts +1 -1
- package/src/run.ts +64 -18
- package/src/scan.ts +22 -8
- package/src/select-priority.ts +3 -3
- package/src/sidecar-apply.ts +1 -1
- package/src/sidecar.ts +138 -11
- package/src/slug-namespace.ts +7 -7
- package/src/spec-complete.ts +2 -3
- package/src/start.ts +53 -59
- package/src/surface-gate.ts +10 -3
- package/src/surface-persist.ts +1 -1
- package/src/tasker-review-loop.ts +4 -3
- package/src/tasking-lock.ts +52 -36
- package/src/tasking.ts +261 -17
- package/src/triage-persist.ts +56 -12
- package/src/vendor/incur/LICENSE +21 -0
- package/src/vendor/incur/README.md +19 -0
- package/src/vendor/incur/agents.ts +392 -0
- package/src/verify-workflow-template.ts +1 -1
- package/src/watch-session.ts +102 -7
- package/src/work-layout.ts +6 -3
- package/src/workspace.ts +1 -1
package/src/run.ts
CHANGED
|
@@ -31,7 +31,11 @@ import {
|
|
|
31
31
|
parseStopSentinel,
|
|
32
32
|
isWorkBranchDiffEmpty,
|
|
33
33
|
emptyDiffStopReason,
|
|
34
|
+
emptyDiffDisposeEnvelope,
|
|
35
|
+
type AgentStopKind,
|
|
34
36
|
} from './agent-stop.js';
|
|
37
|
+
import {surfaceStuckToNeedsAttention} from './needs-attention.js';
|
|
38
|
+
import {resolveItemPathByIdentity} from './item-path.js';
|
|
35
39
|
import {
|
|
36
40
|
classifyFailureCause,
|
|
37
41
|
failureCauseLabel,
|
|
@@ -190,6 +194,7 @@ export type ItemStatus =
|
|
|
190
194
|
| 'surface-unmoved' // the tree-less surface to needs-attention did NOT land on the arbiter (lost the CAS race / no arbiter) — the item is STILL in-progress on the arbiter; retry/resolve
|
|
191
195
|
| 'agent-failed' // the agent ran but produced bad/empty output (the conservative generic), OR the cause is unknown
|
|
192
196
|
| 'transient-infra' // a harness-surfaced model/connection outage (post-retry) or a git/provider outage — RETRY the same work (FAILURE-CAUSE axis)
|
|
197
|
+
| 'needs-reauth' // a credential expired / was revoked (OAuth refresh) — retry cannot help; a human must RE-AUTH (FAILURE-CAUSE axis)
|
|
193
198
|
| 'config-error' // a thrown CORE wiring/config error (e.g. review on, no reviewGate) — fix the WIRING, not the task (FAILURE-CAUSE axis)
|
|
194
199
|
| 'agent-stopped'; // the agent DELIBERATELY stopped (task drifted) OR produced no change — gate + Gate-2 skipped
|
|
195
200
|
|
|
@@ -457,6 +462,7 @@ export async function runOnce(options: RunOnceOptions): Promise<RunOnceResult> {
|
|
|
457
462
|
// are the same agent/run-failure routed to needs-attention, just labelled
|
|
458
463
|
// by cause).
|
|
459
464
|
i.status === 'transient-infra' ||
|
|
465
|
+
i.status === 'needs-reauth' ||
|
|
460
466
|
i.status === 'config-error' ||
|
|
461
467
|
// The surface to needs-attention did NOT land (lost the CAS race); the item
|
|
462
468
|
// is still in-progress on the arbiter — a genuine FAILURE (not a clean
|
|
@@ -709,16 +715,19 @@ async function runOneItem(
|
|
|
709
715
|
// `work/<slug>` tip == the arbiter tip (the kept branch, unchanged,
|
|
710
716
|
// after-commit). The DURABLE artifact is that branch on the arbiter + the
|
|
711
717
|
// main surface (ADR §14: the job worktree is a disposable cache; recovery
|
|
712
|
-
// flows through the branch +
|
|
713
|
-
// worktree). So the surface is purely the
|
|
714
|
-
//
|
|
715
|
-
//
|
|
718
|
+
// flows through the branch + lock surfaces, NOT by editing the
|
|
719
|
+
// worktree). So the surface is purely the lock amend to `state: stuck` +
|
|
720
|
+
// reason (post lock-cutover — no `in-progress/ → needs-attention/` folder
|
|
721
|
+
// move, no branch push, no worktree
|
|
722
|
+
// mutation). Because the branch is provably on the arbiter, the §4 reap
|
|
716
723
|
// predicate still HOLDS and this worktree is reaped — more §14-aligned.
|
|
717
724
|
if (tree.continueRebaseConflict) {
|
|
718
725
|
const reason =
|
|
719
726
|
`continuing the kept ${tree.branch}: rebase onto the latest main ` +
|
|
720
|
-
'conflicted (aborted, never auto-resolved) —
|
|
721
|
-
'
|
|
727
|
+
'conflicted (aborted, never auto-resolved) — run `requeue --reconcile` ' +
|
|
728
|
+
'to non-destructively re-sync the mirror and retry the rebase (keeps ' +
|
|
729
|
+
'the work). Last resort: `requeue --reset` DESTRUCTIVELY discards the ' +
|
|
730
|
+
'branch and starts fresh.';
|
|
722
731
|
updateJobRecord(tree.dir, {state: 'needs-attention', reason});
|
|
723
732
|
const surfaced = await ledgerWrite.applyTreelessNeedsAttentionTransition({
|
|
724
733
|
cwd: tree.dir,
|
|
@@ -747,9 +756,10 @@ async function runOneItem(
|
|
|
747
756
|
const reason =
|
|
748
757
|
`continuing the kept ${tree.branch}: publishing the rebased work branch ` +
|
|
749
758
|
`to the arbiter failed terminally (${tree.continuePushFailure}) — the ` +
|
|
750
|
-
'kept branch is left intact on the arbiter (recoverable);
|
|
751
|
-
'
|
|
752
|
-
'
|
|
759
|
+
'kept branch is left intact on the arbiter (recoverable); ' +
|
|
760
|
+
'`requeue --reconcile` to non-destructively re-sync the mirror and ' +
|
|
761
|
+
'retry once the churn settles (keeps the work). Last resort: ' +
|
|
762
|
+
'`requeue --reset` DESTRUCTIVELY discards the branch and starts fresh.';
|
|
753
763
|
updateJobRecord(tree.dir, {state: 'needs-attention', reason});
|
|
754
764
|
const surfaced = await ledgerWrite.applyTreelessNeedsAttentionTransition({
|
|
755
765
|
cwd: tree.dir,
|
|
@@ -862,18 +872,25 @@ async function runOneItem(
|
|
|
862
872
|
// the arbiter) and SKIPS the gate + Gate-2 (the whole `performIntegration`
|
|
863
873
|
// band) — a clean STOP is NOT "a build that changed nothing".
|
|
864
874
|
const sentinel = parseStopSentinel(agent.output);
|
|
865
|
-
const stopReason =
|
|
875
|
+
const stopReason: {kind: AgentStopKind; reason: string} | undefined =
|
|
866
876
|
sentinel !== undefined
|
|
867
|
-
? sentinel.reason
|
|
877
|
+
? {kind: 'sentinel', reason: sentinel.reason}
|
|
868
878
|
: (await isWorkBranchDiffEmpty({
|
|
869
879
|
cwd: tree.dir,
|
|
870
880
|
arbiter: tree.arbiterRemote,
|
|
871
881
|
env: gitEnv,
|
|
872
882
|
}))
|
|
873
|
-
? emptyDiffStopReason(slug)
|
|
883
|
+
? {kind: 'empty-diff', reason: emptyDiffStopReason(slug)}
|
|
874
884
|
: undefined;
|
|
875
885
|
if (stopReason !== undefined) {
|
|
876
|
-
return await saveAgentStop(
|
|
886
|
+
return await saveAgentStop(
|
|
887
|
+
base,
|
|
888
|
+
tree,
|
|
889
|
+
slug,
|
|
890
|
+
stopReason.reason,
|
|
891
|
+
stopReason.kind,
|
|
892
|
+
ctx,
|
|
893
|
+
);
|
|
877
894
|
}
|
|
878
895
|
|
|
879
896
|
// 5–7 (CONVERGED). The whole gate → review → done-move → commit → rebase →
|
|
@@ -1138,9 +1155,10 @@ function runAgent(
|
|
|
1138
1155
|
* This mirrors `do.ts`'s `saveAgentFailure`: route the failure through the SAME
|
|
1139
1156
|
* ledger write seam's needs-attention transition the gate-fail / integrate-
|
|
1140
1157
|
* conflict bounces use — which (with the arbiter) saves the agent's work as a wip
|
|
1141
|
-
* commit,
|
|
1142
|
-
*
|
|
1143
|
-
* (OBSERVABLE
|
|
1158
|
+
* commit, then — post lock-cutover — amends the item's per-item lock to
|
|
1159
|
+
* `state: stuck` with the failure detail as the reason on the lock entry
|
|
1160
|
+
* (OBSERVABLE; the `needs-attention/` folder move is retired), AND pushes the
|
|
1161
|
+
* `work/<slug>` branch (RECOVERABLE — the durable
|
|
1144
1162
|
* artifact a requeue-continue lands the next agent on; continue-detection reads
|
|
1145
1163
|
* <arbiter>/work/<slug> ahead of main). The push is best-effort — an unreachable
|
|
1146
1164
|
* arbiter leaves the retained worktree + the local commits standing (the genuinely
|
|
@@ -1194,8 +1212,9 @@ function failureCauseToItemStatus(cause: FailureCause): ItemStatus {
|
|
|
1194
1212
|
|
|
1195
1213
|
/**
|
|
1196
1214
|
* Build the HONEST {@link ItemResult} for a CONTINUE-site surface that did NOT
|
|
1197
|
-
* land on the arbiter (`{moved: false}`). The tree-less
|
|
1198
|
-
*
|
|
1215
|
+
* land on the arbiter (`{moved: false}`). The tree-less stuck-lock amend (post
|
|
1216
|
+
* lock-cutover — `state: stuck` + reason, in place of the retired `in-progress/ →
|
|
1217
|
+
* needs-attention/` move) lost the CAS race against a busy arbiter (its
|
|
1199
1218
|
* contention-retry cap exhausted) or had no arbiter to publish to, so the item is
|
|
1200
1219
|
* STILL in-progress on the arbiter — reporting a clean `needs-attention` would
|
|
1201
1220
|
* mislead (it claims the surface landed, when it did not). The DISTINCT
|
|
@@ -1236,9 +1255,36 @@ async function saveAgentStop(
|
|
|
1236
1255
|
tree: IsolatedTree,
|
|
1237
1256
|
slug: string,
|
|
1238
1257
|
reason: string,
|
|
1258
|
+
kind: AgentStopKind,
|
|
1239
1259
|
ctx: OneItemContext,
|
|
1240
1260
|
): Promise<ItemResult> {
|
|
1241
1261
|
updateJobRecord(tree.dir, {state: 'needs-attention', reason});
|
|
1262
|
+
// EMPTY-DIFF branch (spec resolved decision #2, task
|
|
1263
|
+
// `empty-diff-bounce-surfaces-dispose-defaulted-question`): surface a sidecar
|
|
1264
|
+
// carrying an engine-authored DISPOSE-DEFAULTED envelope question on
|
|
1265
|
+
// `<arbiter>/main` (via the PR-1 tree-less surface primitive) and release the
|
|
1266
|
+
// lock — NOT the general lock-stuck bounce path. "Nothing to do" is a
|
|
1267
|
+
// non-deterministic LLM judgement, so a blind requeue would infinite-loop;
|
|
1268
|
+
// the surfaced sidecar + `needsAnswers:true` on `main` parks the item as a
|
|
1269
|
+
// human-visible question a `dispose` answer settles to `tasks/cancelled/`.
|
|
1270
|
+
if (kind === 'empty-diff') {
|
|
1271
|
+
const item = `task:${slug}`;
|
|
1272
|
+
const itemPath = resolveItemPathByIdentity(tree.dir, item);
|
|
1273
|
+
if (itemPath !== undefined) {
|
|
1274
|
+
await surfaceStuckToNeedsAttention({
|
|
1275
|
+
cwd: tree.dir,
|
|
1276
|
+
slug,
|
|
1277
|
+
item,
|
|
1278
|
+
itemPath,
|
|
1279
|
+
reason,
|
|
1280
|
+
envelope: emptyDiffDisposeEnvelope({item, reason}),
|
|
1281
|
+
arbiter: tree.arbiterRemote,
|
|
1282
|
+
env: ctx.gitEnv ?? ctx.env,
|
|
1283
|
+
});
|
|
1284
|
+
return {...base, status: 'agent-stopped', detail: reason};
|
|
1285
|
+
}
|
|
1286
|
+
// Fallthrough (body missing) — fall through to the lock-stuck bounce below.
|
|
1287
|
+
}
|
|
1242
1288
|
await ledgerWrite.applyNeedsAttentionTransition({
|
|
1243
1289
|
cwd: tree.dir,
|
|
1244
1290
|
slug,
|
package/src/scan.ts
CHANGED
|
@@ -280,7 +280,7 @@ export function scoreSpecs(
|
|
|
280
280
|
* re-enumeration — a pure shape map over the already-gated pools.
|
|
281
281
|
*
|
|
282
282
|
* The pool items' `namespace` is the wider {@link SelectedNamespace}. SURFACE by
|
|
283
|
-
* construction only carries `'task'`/`'
|
|
283
|
+
* construction only carries `'task'`/`'spec'` (an observation with no sidecar is a
|
|
284
284
|
* `triage` candidate, never `surface`), so it narrows + drops any non-task/spec
|
|
285
285
|
* defensively. APPLY additionally admits `'observation'` (an answered observation
|
|
286
286
|
* sidecar → apply), and it MUST be kept so the matrix `jq` emits its
|
|
@@ -543,13 +543,22 @@ export function scanRepoPaths(
|
|
|
543
543
|
repoPaths: string[],
|
|
544
544
|
config: Config,
|
|
545
545
|
/**
|
|
546
|
-
* The HELD-SLUG set to SUBTRACT from each repo's pool (`tasks/ready/`)
|
|
547
|
-
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
546
|
+
* The HELD-SLUG set to SUBTRACT from each repo's pool (`tasks/ready/`) AND from
|
|
547
|
+
* the in-place lifecycle triage/surface/apply pools (spec
|
|
548
|
+
* `ledger-status-per-item-lock-refs` US #15; task
|
|
549
|
+
* `in-place-scan-subtracts-held-locked-slugs-from-propose-matrix`). Passed
|
|
550
|
+
* BOTH to {@link scoreItems} AND to {@link gatherLifecycleInPlace}, so a slug
|
|
551
|
+
* whose per-item lock is currently held (state `active`/`stuck`) never leaks
|
|
552
|
+
* into the propose matrix via EITHER the eligible-task pool OR a lifecycle
|
|
553
|
+
* leg — CI would otherwise enumerate a `task:<slug>` surface/apply leg that
|
|
554
|
+
* always loses the claim CAS.
|
|
555
|
+
*
|
|
556
|
+
* This is a WORKING-TREE, OFFLINE scan (it has no arbiter handle to fetch the
|
|
557
|
+
* lock refs from — that is the registry `scan`'s job), so the held set is
|
|
558
|
+
* supplied by the in-place CALLER (which knows its arbiter, e.g.
|
|
559
|
+
* `resolveCwdSection` reads it fail-CLOSED via `heldTaskSlugsStrict`) and
|
|
560
|
+
* DEFAULTS to empty. An empty default preserves the offline read while keeping
|
|
561
|
+
* the seam in place.
|
|
553
562
|
*/
|
|
554
563
|
heldSlugs: Set<string> = new Set(),
|
|
555
564
|
/**
|
|
@@ -590,6 +599,11 @@ export function scanRepoPaths(
|
|
|
590
599
|
surfaceBlockers: resolved.surfaceBlockers,
|
|
591
600
|
surfaceStaging: resolved.surfaceStaging,
|
|
592
601
|
}),
|
|
602
|
+
// Held-slug subtraction on the LIFECYCLE side (task
|
|
603
|
+
// `in-place-scan-subtracts-held-locked-slugs-from-propose-matrix`):
|
|
604
|
+
// symmetric to the `scoreItems` subtraction above, so a held task never
|
|
605
|
+
// leaks into a surface/apply leg either.
|
|
606
|
+
heldSlugs,
|
|
593
607
|
}),
|
|
594
608
|
);
|
|
595
609
|
// The one-slug-one-folder LINT over THIS working tree's `work/` ledger.
|
package/src/select-priority.ts
CHANGED
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Which namespace a selected item names (mirrors the slug-namespace split). The
|
|
52
|
-
* `do` selection only ever produces `task`/`
|
|
52
|
+
* `do` selection only ever produces `task`/`spec`; the `advance` selection ALSO
|
|
53
53
|
* produces `observation` (the lifecycle triage pool, task
|
|
54
54
|
* `advance-autopick-lifecycle-pools`), so a selected lifecycle item carries which
|
|
55
55
|
* rung the driver dispatches to. The widening is BACKWARD-COMPATIBLE: `do` never
|
|
@@ -76,7 +76,7 @@ export interface SelectedItem {
|
|
|
76
76
|
/**
|
|
77
77
|
* A lifecycle-pool selected item (task `advance-autopick-lifecycle-pools`). It is
|
|
78
78
|
* a {@link SelectedItem} — the same shape — carrying the lifecycle namespace
|
|
79
|
-
* (`observation` for triage; `task`/`
|
|
79
|
+
* (`observation` for triage; `task`/`spec` for a `needsAnswers`-blocked item the
|
|
80
80
|
* tick will surface/apply). A distinct alias names the lifecycle pools at the
|
|
81
81
|
* call sites WITHOUT a structural difference (the discriminator is `namespace`).
|
|
82
82
|
*/
|
|
@@ -235,7 +235,7 @@ export function selectPrioritised(
|
|
|
235
235
|
|
|
236
236
|
// The per-pool item lists, keyed by the orderable pool name. NOTE the
|
|
237
237
|
// vocabulary bridge: `build` = the eligible-TASK pool (namespace `task`),
|
|
238
|
-
// `task` = the taskable-spec pool (namespace `
|
|
238
|
+
// `task` = the taskable-spec pool (namespace `spec`) — the action names, not the
|
|
239
239
|
// item namespaces (task `advance-selection-order-config`).
|
|
240
240
|
const byPool: Record<SelectionPool, SelectedItem[]> = {
|
|
241
241
|
build: buildItems,
|
package/src/sidecar-apply.ts
CHANGED
|
@@ -174,7 +174,7 @@ export function applyAtomic(options: ApplyAtomicOptions): ApplyAtomicResult {
|
|
|
174
174
|
writeFileSync(itemAbs, setNeedsAnswersMarker(itemBody, true));
|
|
175
175
|
}
|
|
176
176
|
mkdirSync(dirname(sidecarAbs), {recursive: true});
|
|
177
|
-
writeFileSync(sidecarAbs, serialiseSidecar(sidecar));
|
|
177
|
+
writeFileSync(sidecarAbs, serialiseSidecar(sidecar, {repoRoot: cwd}));
|
|
178
178
|
touched.push(sidecarPath);
|
|
179
179
|
}
|
|
180
180
|
|
package/src/sidecar.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import {existsSync} from 'node:fs';
|
|
2
|
+
import {join} from 'node:path';
|
|
1
3
|
import {parseSlugArg, type SlugNamespace} from './slug-namespace.js';
|
|
2
|
-
import {workItemRel} from './work-layout.js';
|
|
4
|
+
import {workItemRel, type WorkFolderKey} from './work-layout.js';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* The **question/answer SIDECAR contract** (spec `advance-loop`, task
|
|
@@ -72,7 +74,7 @@ import {workItemRel} from './work-layout.js';
|
|
|
72
74
|
* The item-types a sidecar can key onto (the slug-namespace + obs).
|
|
73
75
|
*
|
|
74
76
|
* HARD CUTOVER (spec `prd-to-spec-vocabulary-cutover-and-migration-command`,
|
|
75
|
-
* contract step): the legacy
|
|
77
|
+
* contract step): the legacy ''prd'' type member is GONE — the parent-spec type
|
|
76
78
|
* is `'spec'` only. A `spec:<slug>` identity keys onto the `spec-<slug>`
|
|
77
79
|
* lock/sidecar entry. The on-disk `prd-<slug>.md` sidecar FILE (dorfl's
|
|
78
80
|
* not-yet-converted data) is still probed by {@link sidecarPathCandidates} as a
|
|
@@ -88,13 +90,19 @@ export type SidecarType = 'spec' | 'task' | 'observation';
|
|
|
88
90
|
* sniffing the shape of another field. Absent ⇒ the existing binary content
|
|
89
91
|
* entry (every pre-`kind` sidecar parses + renders byte-identically).
|
|
90
92
|
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
93
|
+
* A TYPED FIELD is the PERMANENT home for the kind axis (not an interim step
|
|
94
|
+
* toward kind subfolders). Encoding kind in the PATH — either a filename prefix
|
|
95
|
+
* (`merge-<type>-<slug>.md`) or a subfolder (`work/questions/merge/…`) — was
|
|
96
|
+
* deliberately REJECTED: kind is a MUTABLE axis (one item can carry a `spec`
|
|
97
|
+
* question pre-build, then a `merge` question at land time), but `sidecarPathFor`
|
|
98
|
+
* is a pure function of item IDENTITY alone and does not know the kind, so a
|
|
99
|
+
* path that encodes kind would make identity-only lookups SILENTLY miss a
|
|
100
|
+
* sidecar filed under a different kind. So kind lives INSIDE the file (this
|
|
101
|
+
* field), the path stays identity-keyed, and per-kind queues are RENDERED by the
|
|
102
|
+
* tool (`status`/`scan`) rather than by the directory tree. See the observation
|
|
103
|
+
* `questions-folder-rename-and-kind-axis-prefix-vs-subfolder-2026-06-21` (its
|
|
104
|
+
* round-2 conclusion + the recorded answers) and `SURFACE-PROTOCOL.md`. The
|
|
105
|
+
* field is read in exactly ONE place (the apply dispatch).
|
|
98
106
|
*
|
|
99
107
|
* A mistyped/unknown `kind=` token parses to `undefined` (silent-on-malformed,
|
|
100
108
|
* mirroring the retired `disposition` precedent), never a throw, never a
|
|
@@ -274,7 +282,7 @@ export function sidecarPathFor(identity: string): string {
|
|
|
274
282
|
* `spec`-typed identity ALSO probes the legacy `prd-<slug>.md` as a fallback, and
|
|
275
283
|
* a reader takes the FIRST candidate that exists. This is a FILE-PATH DATA alias
|
|
276
284
|
* the migration command removes (it converts `prd-<slug>.md → spec-<slug>.md` on
|
|
277
|
-
* disk); it is NOT the `SlugNamespace`/`SidecarType`
|
|
285
|
+
* disk); it is NOT the `SlugNamespace`/`SidecarType` ''prd'' type member. Every
|
|
278
286
|
* non-`spec` type has a single candidate (its canonical path), unchanged.
|
|
279
287
|
*/
|
|
280
288
|
export function sidecarPathCandidates(identity: string): string[] {
|
|
@@ -291,6 +299,77 @@ export function sidecarPathCandidates(identity: string): string[] {
|
|
|
291
299
|
/** The fixed answer marker the human types prose under. */
|
|
292
300
|
const ANSWER_MARKER = '**Your answer** (write below this line):';
|
|
293
301
|
|
|
302
|
+
/**
|
|
303
|
+
* The lifecycle folders a given item type may currently reside in — the
|
|
304
|
+
* search set the {@link serialiseSidecar} human-visible link line scans to
|
|
305
|
+
* locate the item at write-time. Deliberately INCLUSIVE of the terminal
|
|
306
|
+
* folders (`done`, `cancelled`, `prds-dropped`) so a sidecar still-being-
|
|
307
|
+
* serialised for a finished item still emits a clickable link. Kept LOCAL to
|
|
308
|
+
* this module (rather than reused from `advance.ts`) because this set is the
|
|
309
|
+
* "where might the item CURRENTLY be on disk?" question, which is broader
|
|
310
|
+
* than advance's rung-classifier reach.
|
|
311
|
+
*
|
|
312
|
+
* DECISION (sidecar-visible-item-link): the task set is the four DURABLE
|
|
313
|
+
* folders `tasks-ready` / `done` / `cancelled` / `tasks-backlog` and
|
|
314
|
+
* deliberately EXCLUDES `in-progress` / `needs-attention`. Those two are NOT
|
|
315
|
+
* durable folders — they are retired transient lock-ref state (ADR
|
|
316
|
+
* `needs-attention-folder-cutover-followup-nits`, see the `TASK_LIFECYCLE_FOLDERS`
|
|
317
|
+
* JSDoc in `work-layout.ts`): a stuck task's BODY rests in `tasks/ready/`
|
|
318
|
+
* while the lock carries `state: stuck`, so no task body ever lives under
|
|
319
|
+
* `work/in-progress/` or `work/needs-attention/` for this scan to find. An
|
|
320
|
+
* earlier WIP of this task listed both; narrowing to the durable set matches
|
|
321
|
+
* the current on-disk reality and avoids scanning phantom folders.
|
|
322
|
+
* ALTERNATIVE considered: reuse `TASK_LIFECYCLE_FOLDERS` directly — rejected
|
|
323
|
+
* because it still carries the legacy `in-progress` entry and omits the
|
|
324
|
+
* `cancelled` / `tasks-backlog` folders a sidecar link may need to reach.
|
|
325
|
+
*/
|
|
326
|
+
const LINK_LIFECYCLE_FOLDERS: Record<SidecarType, readonly WorkFolderKey[]> = {
|
|
327
|
+
task: ['tasks-ready', 'done', 'cancelled', 'tasks-backlog'],
|
|
328
|
+
spec: ['specs-ready', 'specs-tasked', 'specs-proposed', 'specs-dropped'],
|
|
329
|
+
observation: ['observations'],
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Look up the item's CURRENT on-disk repo-relative path by scanning the
|
|
334
|
+
* lifecycle folders its type may reside in, in a documented precedence order.
|
|
335
|
+
* Returns `undefined` when the item is not found in ANY folder — the
|
|
336
|
+
* serialiser's "harmless fallback" (omit the link) branch.
|
|
337
|
+
*
|
|
338
|
+
* PURE lookup: no throws, no side effects. Every call is a fresh scan — the
|
|
339
|
+
* link SELF-HEALS on the next `serialise` after a `git mv` between folders
|
|
340
|
+
* (identity-keyed sidecar; NO lock-step move).
|
|
341
|
+
*/
|
|
342
|
+
function findItemRelPath(
|
|
343
|
+
repoRoot: string,
|
|
344
|
+
type: SidecarType,
|
|
345
|
+
slug: string,
|
|
346
|
+
): string | undefined {
|
|
347
|
+
for (const folder of LINK_LIFECYCLE_FOLDERS[type]) {
|
|
348
|
+
const rel = workItemRel(folder, `${slug}.md`);
|
|
349
|
+
if (existsSync(join(repoRoot, rel))) {
|
|
350
|
+
return rel;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return undefined;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Render the human-visible Markdown link line, from the sidecar's fixed path
|
|
358
|
+
* (`work/questions/<type>-<slug>.md`) to the item's current `work/<folder>/
|
|
359
|
+
* <slug>.md`. Since both live under `work/`, the sidecar-relative link is
|
|
360
|
+
* `../<folder-name>/<slug>.md` — the `..` climbs out of `questions/` and the
|
|
361
|
+
* `<folder-name>` (which may contain a `/`, e.g. `tasks/ready`) drops back in.
|
|
362
|
+
*/
|
|
363
|
+
function renderItemLinkLine(item: string, itemRel: string): string {
|
|
364
|
+
// itemRel is `work/<folder-name>/<slug>.md`; strip the leading `work/` and
|
|
365
|
+
// prefix with `../` so the link is relative to `work/questions/<file>.md`.
|
|
366
|
+
const prefix = 'work/';
|
|
367
|
+
const target = itemRel.startsWith(prefix)
|
|
368
|
+
? `../${itemRel.slice(prefix.length)}`
|
|
369
|
+
: itemRel;
|
|
370
|
+
return `Item: [\`${item}\`](${target})`;
|
|
371
|
+
}
|
|
372
|
+
|
|
294
373
|
// --- Parse ----------------------------------------------------------------
|
|
295
374
|
|
|
296
375
|
/** Pull the next monotonic id given the highest existing id number. */
|
|
@@ -582,7 +661,10 @@ function blockquote(value: string): string[] {
|
|
|
582
661
|
* `parseSidecar(serialiseSidecar(m))` recovers an equal MODEL; re-serialising
|
|
583
662
|
* canonicalises the text.
|
|
584
663
|
*/
|
|
585
|
-
export function serialiseSidecar(
|
|
664
|
+
export function serialiseSidecar(
|
|
665
|
+
model: SidecarModel,
|
|
666
|
+
options: SerialiseSidecarOptions = {},
|
|
667
|
+
): string {
|
|
586
668
|
const out: string[] = [];
|
|
587
669
|
const identityParts = [
|
|
588
670
|
`item=${model.item}`,
|
|
@@ -592,6 +674,21 @@ export function serialiseSidecar(model: SidecarModel): string {
|
|
|
592
674
|
];
|
|
593
675
|
out.push(`<!-- dorfl-sidecar: ${identityParts.join(' ')} -->`);
|
|
594
676
|
|
|
677
|
+
// Human-visible Markdown link line — placed AFTER the identity comment and
|
|
678
|
+
// BEFORE the first `## ` heading, i.e. in the parser's ignored preamble
|
|
679
|
+
// region. Regenerated on every serialise from the item's CURRENT on-disk
|
|
680
|
+
// location; NEVER round-tripped through the parsed model (the link line is
|
|
681
|
+
// write-only cosmetic output, per the source observation). If the item is
|
|
682
|
+
// not resolvable on disk, we simply OMIT the line (harmless fallback) — a
|
|
683
|
+
// broken link would be more confusing than no link.
|
|
684
|
+
if (options.repoRoot !== undefined) {
|
|
685
|
+
const itemRel = findItemRelPath(options.repoRoot, model.type, model.slug);
|
|
686
|
+
if (itemRel !== undefined) {
|
|
687
|
+
out.push('');
|
|
688
|
+
out.push(renderItemLinkLine(model.item, itemRel));
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
595
692
|
model.entries.forEach((entry) => {
|
|
596
693
|
out.push('');
|
|
597
694
|
const heading = entry.id.replace(/^q/, 'Q');
|
|
@@ -634,6 +731,36 @@ export function serialiseSidecar(model: SidecarModel): string {
|
|
|
634
731
|
return out.join('\n');
|
|
635
732
|
}
|
|
636
733
|
|
|
734
|
+
/**
|
|
735
|
+
* Options for {@link serialiseSidecar}. The optional `repoRoot` opts the
|
|
736
|
+
* caller into emitting the human-visible Markdown link line at the top of
|
|
737
|
+
* the sidecar (pointing at the item's current `work/<folder>/<slug>.md`).
|
|
738
|
+
* Callers that don't have a repo root (e.g. pure format tests) simply omit
|
|
739
|
+
* it and the serialiser emits no link — the parse is unaffected either way.
|
|
740
|
+
*
|
|
741
|
+
* DECISION (sidecar-visible-item-link): `repoRoot` is OPTIONAL and defaults
|
|
742
|
+
* to NO link, rather than being a required argument threaded through every
|
|
743
|
+
* caller. Only the two WRITING call sites (`applyAtomic` in
|
|
744
|
+
* `sidecar-apply.ts`, `persistSurfacedQuestions` in `surface-persist.ts`)
|
|
745
|
+
* pass `cwd`; any other current/future caller of `serialiseSidecar` silently
|
|
746
|
+
* emits no link line. This keeps the many pure-format / round-trip tests
|
|
747
|
+
* trivial (they need no on-disk repo) and keeps the link a write-only
|
|
748
|
+
* cosmetic concern of the persist paths. TRADE-OFF: a new writing caller must
|
|
749
|
+
* remember to pass `repoRoot` to get the link; ALTERNATIVE considered — make
|
|
750
|
+
* it required — rejected because it would force every format-only caller and
|
|
751
|
+
* test to fabricate a repo root purely to reach the same no-link output.
|
|
752
|
+
*/
|
|
753
|
+
export interface SerialiseSidecarOptions {
|
|
754
|
+
/**
|
|
755
|
+
* Absolute or relative path to the repository root, so the serialiser can
|
|
756
|
+
* scan `work/<lifecycle-folder>/<slug>.md` for the item and render a
|
|
757
|
+
* clickable relative link line. When omitted (or when the item cannot be
|
|
758
|
+
* located), the link line is omitted — a harmless fallback, never a
|
|
759
|
+
* broken link.
|
|
760
|
+
*/
|
|
761
|
+
repoRoot?: string;
|
|
762
|
+
}
|
|
763
|
+
|
|
637
764
|
// --- Append ---------------------------------------------------------------
|
|
638
765
|
|
|
639
766
|
/** A new question to append (no id — the appender mints the next monotonic id). */
|
package/src/slug-namespace.ts
CHANGED
|
@@ -43,8 +43,8 @@ import {ledgerRead, type LedgerReadStrategy} from './ledger-read.js';
|
|
|
43
43
|
* {@link resolveAdvanceArg}).
|
|
44
44
|
*
|
|
45
45
|
* HARD CUTOVER (spec `prd-to-spec-vocabulary-cutover-and-migration-command`,
|
|
46
|
-
* contract step): the legacy
|
|
47
|
-
* is `'spec'` only. A
|
|
46
|
+
* contract step): the legacy ''prd'' member is GONE — the parent-spec namespace
|
|
47
|
+
* is `'spec'` only. A ''prd:<slug>'' arg no longer parses to a namespace (it falls
|
|
48
48
|
* through to a bare literal slug), and a `work/prd-<slug>` branch ref no longer
|
|
49
49
|
* parses. No back-compat alias (the clean-break stance).
|
|
50
50
|
*/
|
|
@@ -102,7 +102,7 @@ export function workBranchRef(
|
|
|
102
102
|
* anchors the optional producer prefix BEFORE the type alternation, so
|
|
103
103
|
* `work/intake-task-foo` resolves to
|
|
104
104
|
* `{producer:'intake', namespace:'task', slug:'foo'}` (the `slug` never
|
|
105
|
-
* swallows the `intake-`/`task-` prefixes). The old
|
|
105
|
+
* swallows the `intake-`/`task-` prefixes). The old ''slice''/''brief''/''prd'' types
|
|
106
106
|
* are NOT in the alternation, so a pre-rename `work/prd-foo` ref returns
|
|
107
107
|
* `undefined` (the clean-break stance: no migration-window alias).
|
|
108
108
|
*/
|
|
@@ -112,7 +112,7 @@ export function parseWorkBranchRef(
|
|
|
112
112
|
| {producer?: BranchProducer; namespace: SlugNamespace; slug: string}
|
|
113
113
|
| undefined {
|
|
114
114
|
// HARD CUTOVER (spec `prd-to-spec-vocabulary-cutover-and-migration-command`,
|
|
115
|
-
// contract step): the type alternation is `task|spec` ONLY — the legacy
|
|
115
|
+
// contract step): the type alternation is `task|spec` ONLY — the legacy ''prd''
|
|
116
116
|
// token is GONE, so a pre-rename `work/prd-<slug>` ref returns `undefined` (no
|
|
117
117
|
// migration-window alias; the clean-break stance).
|
|
118
118
|
const match = /^work\/(?:(intake)-)?(task|spec)-(.+)$/.exec(branch);
|
|
@@ -155,7 +155,7 @@ const TASK_PREFIX = 'task:';
|
|
|
155
155
|
/**
|
|
156
156
|
* The parent-spec namespace prefix: `spec:<slug>` → `{explicit: 'spec'}`. HARD
|
|
157
157
|
* CUTOVER (spec `prd-to-spec-vocabulary-cutover-and-migration-command`): the
|
|
158
|
-
* legacy
|
|
158
|
+
* legacy ''prd:'' prefix is GONE — a ''prd:<slug>'' arg falls through to a bare
|
|
159
159
|
* literal slug (no namespace), the clean-break stance.
|
|
160
160
|
*/
|
|
161
161
|
const SPEC_PREFIX = 'spec:';
|
|
@@ -184,8 +184,8 @@ export class SlugResolutionError extends Error {
|
|
|
184
184
|
* slug. PURE string work, no existence check: `task:foo` → explicit task,
|
|
185
185
|
* `spec:foo` → explicit spec, `foo` → bare (`explicit: undefined`). The prefix
|
|
186
186
|
* match is case-sensitive and exact (`task:`/`spec:`); a slug like `tasked` is
|
|
187
|
-
* NOT a prefix and stays bare. HARD CUTOVER: the legacy
|
|
188
|
-
* namespace prefix anymore —
|
|
187
|
+
* NOT a prefix and stays bare. HARD CUTOVER: the legacy ''prd:'' prefix is not a
|
|
188
|
+
* namespace prefix anymore — ''prd:foo'' stays a bare literal slug.
|
|
189
189
|
*/
|
|
190
190
|
export function parseSlugArg(arg: string): ParsedSlugArg {
|
|
191
191
|
if (arg.startsWith(TASK_PREFIX)) {
|
package/src/spec-complete.ts
CHANGED
|
@@ -27,9 +27,8 @@ import {
|
|
|
27
27
|
* claim ledger (`ledger-read.ts` resolves claim-STATE, a different concern). It
|
|
28
28
|
* reuses {@link parseFrontmatter} (the `spec:` field) rather than hand-rolling a YAML
|
|
29
29
|
* parse, and scans the task lifecycle folders directly: `work/tasks/ready/`,
|
|
30
|
-
* `work/in-progress/`,
|
|
31
|
-
*
|
|
32
|
-
* means the spec is not yet complete.
|
|
30
|
+
* `work/in-progress/`, and `work/done/`. A task that has NOT yet landed in
|
|
31
|
+
* `work/done/` (still in backlog / in-progress) means the spec is not yet complete.
|
|
33
32
|
*/
|
|
34
33
|
|
|
35
34
|
/** The task lifecycle folders a `spec:<slug>` task can reside in. */
|