Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .claude/skills/dld-common/scripts/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ get_run_dir() {

# Fail with a clear message when jq is unavailable.
# Goal run state is JSON; the dld-goal scripts require jq to read and mutate it.
# Print the caller script's usage from its header comment, stopping at the
# first line that is not a comment. Scripts source common.sh, so the script
# whose usage we want is BASH_SOURCE[1]; BASH_SOURCE[0] is common.sh itself.
usage() {
sed -n '2,$p' "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}" \
| sed -n '/^[^#]/q; p' \
| sed 's/^# \{0,1\}//' >&2
}

require_jq() {
if ! command -v jq >/dev/null 2>&1; then
echo "Error: jq is required by the dld-goal scripts but was not found on PATH." >&2
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/dld-goal/scripts/append-event.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ DATA="{}"
while [[ $# -gt 0 ]]; do
case "$1" in
--data) DATA="$2"; shift 2 ;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
esac
done

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/dld-goal/scripts/block-item.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ while [[ $# -gt 0 ]]; do
--reason) REASON="$2"; shift 2 ;;
--question) QUESTION="$2"; shift 2 ;;
--force) FORCE=true; shift ;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
esac
done

Expand Down
8 changes: 7 additions & 1 deletion .claude/skills/dld-goal/scripts/create-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ while [[ $# -gt 0 ]]; do
--max-minutes) MAX_MINUTES="$2"; shift 2 ;;
--review) REVIEW="$2"; shift 2 ;;
--body-stdin) READ_STDIN=true; shift ;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
*) if [[ "$1" != -* ]]; then
usage
else
echo "Unknown option: $1" >&2
usage
fi
exit 1 ;;
esac
done

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/dld-goal/scripts/guard-preconditions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ while [[ $# -gt 0 ]]; do
case "$1" in
--decisions) DECISIONS="$2"; shift 2 ;;
--base) BASE="$2"; shift 2 ;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
esac
done

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/dld-goal/scripts/resolve-block.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ while [[ $# -gt 0 ]]; do
case "$1" in
--answer) ANSWER="$2"; shift 2 ;;
--action) ACTION="$2"; shift 2 ;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
esac
done

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/dld-goal/scripts/run-state.sh
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ case "$COMMAND" in
--decisions) DECISIONS="$2"; shift 2 ;;
--check) CHECKS="$(jq --argjson c "$(parse_check "$2")" '. + [$c]' <<<"$CHECKS")"; shift 2 ;;
--annotation) ANNOTATIONS="$(jq --arg a "$2" '. + [$a]' <<<"$ANNOTATIONS")"; shift 2 ;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
esac
done
if [[ -z "$DECISIONS" ]]; then
Expand Down
3 changes: 2 additions & 1 deletion decisions/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

| ID | Title | Status | Tags |
|----|-------|--------|------|
| DL-012 | Amend DL-007: correct the delegated script list | accepted | dld-goal, architecture, state |
| DL-011 | Run visibility is layered: status line, fixed-height widget, transcript cards, board overlay | proposed | dld-goal, extension, ui |
| DL-010 | Compaction during a run is assembled deterministically from disk, never model-summarised | proposed | dld-goal, extension, context |
| DL-009 | Child-session rotation is a re-entrant controller driven by a typed tool, verified against disk | proposed | dld-goal, extension, architecture |
| DL-008 | In-session continuation fires on agent_end behind idle, token, and bounds gates | proposed | dld-goal, extension, execution |
| DL-007 | The extension reads run state directly but delegates every mutation to the skill scripts | proposed | dld-goal, architecture, state |
| DL-007 | The extension reads run state directly but delegates every mutation to the skill scripts | accepted | dld-goal, architecture, state |
| DL-006 | dld-kit is a Pi package: TypeScript extension, no build step, bun test | accepted | dld-goal, packaging, tooling |
| DL-005 | The skill owns DLD semantics; a Pi extension owns loop mechanics | accepted | dld-goal, architecture |
| DL-004 | Runs halt on unsafe preconditions and escalate blocked items as operator questions in the run | accepted | dld-goal, safety, execution |
Expand Down
15 changes: 13 additions & 2 deletions decisions/records/DL-007.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
id: DL-007
title: "The extension reads run state directly but delegates every mutation to the skill scripts"
timestamp: 2026-08-21T12:18:07Z
status: proposed
status: accepted
supersedes: []
amends: []
tags: [dld-goal, architecture, state]
references: []
references:
- path: extensions/dld-goal/run-state.ts
symbol: stateMutations
- path: extensions/dld-goal/run-state.test.ts
---

## Context
Expand All @@ -15,6 +18,8 @@ Run state is a JSON document plus an append-only event log (DL-001), mutated tod

DL-005 named the hazard when it split skill from extension: "Two implementations of the state transitions will exist — bash with jq in the skill, TypeScript in the extension. They must agree." A schema documented in prose is not a mechanism; it is an intention that decays.

The run contract promises atomicity through a temp file plus rename, which is why reads may be unsynchronized but a corrupted document is never expected — a parse failure is evidence of a bug, not a timing artefact.

## Decision

The extension never writes run state. It reads `state.json` directly, and delegates every mutation to the skill scripts by executing them:
Expand All @@ -24,6 +29,10 @@ The extension never writes run state. It reads `state.json` directly, and delega

No TypeScript code constructs a state document, sets an item status, or appends an event. If the extension needs a new mutation, it is added to the scripts and covered by bats, then called.

Unknown values are rejected at the boundary rather than tolerated: a state document whose `status` is not one of the five known values, or whose items have an unknown item status, fails to parse with `invalid-shape`. The scripts write only enumerated values; a document with anything else has been modified by a bug or by a human hand, and silently accepting it is how drift becomes invisible.

Events are read directly too — a `readEventsFrom` that parses `events.jsonl` line by line. The original plan had this go through `jq` via a shell; that was scrapped because it reintroduced a string-template path and missed line diagnostics, when the point of DL-003 is to avoid exec through a string.

## Rationale

This removes the dual-implementation problem rather than managing it. There is exactly one implementation of every transition, it is already covered by the existing suite, and no amount of drift between two languages is possible because there is no second implementation.
Expand All @@ -40,4 +49,6 @@ The extension cannot run where the scripts cannot — no bash, no jq, no reposit

Error handling crosses a process boundary. The extension must surface script exit codes and stderr rather than throwing typed errors, and script messages become user-facing text in the harness.

The mutation envelope returns the raw script output verbatim — stderr when stdout is empty. That is not incidental: `next-item.sh` uses exit code 2 for a blocked item and writes the operator question to stderr, so a delegation layer that summarised stderr would have cut that question off before the user ever saw it.

Adding a mutation now touches two places: a script plus its bats tests, then the call site. Slower than writing TypeScript inline, and deliberately so.
37 changes: 37 additions & 0 deletions decisions/records/DL-012.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
id: DL-012
title: "Amend DL-007: correct the delegated script list"
timestamp: 2026-08-21T14:20:51Z
status: accepted
supersedes: []
amends: [DL-007]
tags: [dld-goal, architecture, state]
references: []
---

## Context

DL-007 split read from write: the extension parses run state directly and delegates every mutation to the skill scripts. As originally written, the Decision named the script set as `run-state.sh, append-event.sh, block-item.sh, resolve-block.sh, verify-item.sh`.

The implementing module in `extensions/dld-goal/run-state.ts` delegates to that list minus `verify-item.sh` and plus two more the contract also needs: `verify-hashes.sh` (drift detection) and `next-item.sh` (item selection). The omission would have left those two unaccountable — the extension would have been calling scripts not named in the accepted decision, while a script no longer used would have been recorded as still part of the contract.

## Decision

Amend DL-007 to replace the named script list with:

- `run-state.sh`
- `append-event.sh`
- `block-item.sh`
- `resolve-block.sh`
- `verify-hashes.sh`
- `next-item.sh`

`verify-item.sh` is not delegated to. The rest of DL-007 stands.

## Rationale

The list has to match what the module actually calls, because the point of DL-007 is that no mutation goes through anything but a named, tested script. An inaccurate list is not something the extension can be checked against.

## Consequences

The amendment mechanism exists for exactly this: DL-007 was accepted and then the review found a mismatch between the record and the code. Correcting the accepted record through `amends` preserves the accepted body as the audit trail instead of rewriting it under the same ID.
Loading
Loading