Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
8d927b7
spec+plan: reasoning-degradation ledger (devague#97)
OriNachum Jul 29, 2026
bad40ad
t1: Lapse domain model on Frame (LapseRecord, lapse codes, schema v5)
OriNachum Jul 29, 2026
4a47f74
merge(t1): lapse domain model on Frame — LapseRecord, LAPSE_CODES, sc…
OriNachum Jul 29, 2026
39e7bb6
t4: pin gate inertness against the lapse ledger (issue #97)
OriNachum Jul 29, 2026
7b1a7ca
t2: lapse CLI verb — file, list, adjudicate (devague#97)
OriNachum Jul 29, 2026
1788585
t3: render the lapse ledger — show and summary consume, spec stays un…
OriNachum Jul 29, 2026
2bc7620
merge(t2): lapse CLI verb — file, list, adjudicate (devague#97)
OriNachum Jul 29, 2026
0f67775
merge(t3): render the lapse ledger — show and summary consume, spec s…
OriNachum Jul 29, 2026
5259c33
merge(t4): pin gate inertness against the lapse ledger (issue #97)
OriNachum Jul 29, 2026
c9db693
t6: docs, contract, changelog, version (0.22.0, issue #97)
OriNachum Jul 29, 2026
ef2ab42
t5: skills sweep — producer, consumer, and the subagent boundary (dev…
OriNachum Jul 29, 2026
e95b7af
merge(t5): skills sweep — producer, consumer, and the subagent bounda…
OriNachum Jul 29, 2026
b92f982
merge(t6): docs, contract, changelog, version (0.22.0, issue #97)
OriNachum Jul 29, 2026
8856938
deviate(d1): split-plan --write escapes verbatim task text (devague#97)
OriNachum Jul 29, 2026
b72ae3a
delivery: reasoning-degradation ledger (devague#97)
OriNachum Jul 29, 2026
d6b9326
fix(lapse): refuse record flags given without a positional 'what' (#1…
OriNachum Jul 29, 2026
2a0555e
delivery: record the Qodo fix, l3/l4, and the real SonarCloud result
OriNachum Jul 29, 2026
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
26 changes: 23 additions & 3 deletions .claude/skills/assign-to-workforce/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,14 @@ Once the human approves, the main agent fans out each wave in order:
- Instruction to work **test-first** (TDD): write the failing test(s) that
match the acceptance criteria before implementing.
- Instruction to commit its work to the worktree branch.
- Instruction to **report, never file**, any reasoning-degradation lapse it
notices in its own work — a skipped check, an assumption standing in for
a real measurement, an unverified grader, missing provenance, or another
of the six `LAPSE_CODES` in `devague/frame.py`. The task agent names it
in its transcript or final report; it never runs `devague lapse` itself,
because it never runs any devague command inside its worktree (see the
hard rule below). The **main agent** files the record (`devague lapse
"<what>" --code <code> --origin llm`) once the worktree is reconciled.

3. **Same-wave tasks run in parallel** (within-wave tasks have no
inter-task dependency; the dependency graph guarantees this). Same-file
Expand Down Expand Up @@ -317,9 +325,21 @@ These protect the human-gate contract and the TDD guarantee.
baseline was already broken — fix the baseline first.
- **Human does not gate per-task merges.** The TDD contract replaces the
human here. Do not pause for human approval between wave tasks.
- **devague CLI is not orchestrated.** `devague plan waves` is read-only
scheduling metadata (#20). Never run `devague plan` commands inside a task
worktree to "mark a task done" or modify plan state from a subagent.
- **No devague move runs inside a task worktree — not just `devague plan`.**
`devague plan waves` is read-only scheduling metadata (#20); more broadly, a
task agent never runs any devague command in its worktree, including
`devague lapse`. If a task agent notices its own reasoning degraded — a
skipped check, an assumption standing in for a real measurement, an
unverified grader, missing provenance, or another `LAPSE_CODES` case
(`devague/frame.py`) — it reports the degradation in its transcript or
final report; it does not file it. The **main agent** files that record
after reconciling the worktree (`devague lapse "<what>" --code <code>
--origin llm`), the same way it alone runs every plan-mutating move —
mirroring the `/scope` subagent boundary, where exploration subagents
report and only the main agent runs a `devague` move (#79/#91). Adjudicating
a filed lapse (`devague lapse --confirm`/`--reject`) is the same human who
already owns gate 2/3 — no new role — typically exercised once the run
reaches `/summarize-delivery`.
- **Three gates only.** The human's gates are: (1) the exported spec, (2) the
implementation split plan, (3) the final PR. No silent fourth gate.
- **No LLM calls in the devague CLI.** The CLI is deterministic. This skill
Expand Down
42 changes: 40 additions & 2 deletions .claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,16 @@ _URL_TRAILING_PUNCT = ".,;:!?'\""
_CODE_SPAN_RE = re.compile(r"`[^`]*`")
_ANGLE_TOKEN_RE = re.compile(r"<[^<>\s][^<>]*>")

# The `md_safe_text` half of the upstream helper. Task instructions routinely
# name underscore-bearing identifiers (`__init__.py`, `_build_parser`), which
# markdown reads as emphasis/strong (MD037, MD050) unless wrapped in a code
# span. Ported rather than imported for the same portability reason as above.
_IDENTIFIER_EXTENSIONS = "py|md|rst|json|ya?ml|toml|cfg|ini|sh|js|ts|rb|go"
_IDENTIFIER_RE = re.compile(rf"[A-Za-z0-9_]*_[A-Za-z0-9_]*(?:\.(?:{_IDENTIFIER_EXTENSIONS}))?")
_STRAY_CONTROL_CHAR_RE = re.compile(r"(?<!\\)([*\[\]])")
_STRAY_BACKTICK_RE = re.compile(r"(?<!\\)`")
_PROTECTED_RE = re.compile(r"`[^`]*`|<https?://[^\s<>]*>|https?://[^\s<>()]+")


def _strip_url_trailing_punct(url):
trail = ""
Expand Down Expand Up @@ -425,12 +435,40 @@ def heading_safe(text):
return _HEADING_TRAILING_PUNCT_RE.sub("", autolink_urls(text))


def _escape_segment(segment):
"""Escape + wrap one non-code-span slice. Order matters for idempotence:
stray backticks and control chars first, identifier wrapping last."""
segment = _STRAY_BACKTICK_RE.sub(r"\\`", segment)
segment = _STRAY_CONTROL_CHAR_RE.sub(r"\\\1", segment)
return _IDENTIFIER_RE.sub(lambda m: f"`{m.group(0)}`", segment)


def md_safe_text(text):
"""Wrap underscore/dunder identifiers in code spans and backslash-escape
the remaining stray control characters. Code spans and URLs pass through
byte-for-byte; idempotent, so composing it with the helpers above in
either order is safe."""
if not text:
return text
parts = []
last = 0
for m in _PROTECTED_RE.finditer(text):
parts.append(_escape_segment(text[last : m.start()]))
parts.append(m.group(0)) # code span or URL: verbatim, never touched
last = m.end()
parts.append(_escape_segment(text[last:]))
result = "".join(parts)
if result.startswith("#"):
result = "\\" + result
return result


def safe_body(text):
return autolink_urls(backtick_bare_angle_brackets(text))
return md_safe_text(autolink_urls(backtick_bare_angle_brackets(text)))


def safe_heading(text):
return heading_safe(backtick_bare_angle_brackets(text))
return heading_safe(md_safe_text(backtick_bare_angle_brackets(text)))


def parse_existing_assignments(path):
Expand Down
32 changes: 26 additions & 6 deletions .claude/skills/challenge/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,11 @@ through these structured lenses (from issue 73):
4. **Route every finding through an existing move.** Use the routing table
below. Everything the agent proposes carries `--origin llm` and lands
`proposed` — the pass cannot silently convert speculation into confirmed
requirements.
requirements. If the pass itself notices its own reasoning degraded while
sweeping — a skipped check, an assumption standing in for a real
measurement — that is not a finding about the spec; self-report it the same
moment via `devague lapse --origin llm` (the routing table's last row,
issue #97).
5. **Let the human adjudicate.** `devague review` lists every proposal with
ids; `devague confirm` / `devague reject` / `devague question --resolve`
are user-only decisions. This is the existing spec gate doing its job.
Expand Down Expand Up @@ -160,6 +164,21 @@ move to land in:
| unexamined surfaces | what this pass did not (or could not) look at | `devague scope "<surface>" --finding "<what was and wasn't examined, and why>"` |
| residual surprise risk | uncertainty that survives the pass | `park` on the frame while speccing; `devague plan risk --kind <kind>` once the plan exists |
| resilience measures | containment, rollback, recovery the surprise cost demands | spec-side `capture --kind requirement` / `--kind boundary`; plan-side `devague plan risk` (see below) |
| reasoning degradation *(not one of issue 73's six — the pass's own self-report, not a spec finding)* | a shortcut, skipped check, or degraded reasoning the pass itself made while sweeping | `devague lapse "<what>" --code <code> [--skipped "<check>"] [--ref <lens/surface>] --origin llm` |

Every row above except the last lands **proposed content about the spec** —
a claim, a question, a park, a scope finding — that the human adjudicates
through the spec gate. The `lapse` row is different in kind: it is not a
finding about the spec at all but the agent's own self-report that its
reasoning degraded while running the pass — an assumption stood in for a real
measurement, a check the pass meant to run was skipped, an unverified
grader, missing provenance, and the other codes in `devague/frame.py`'s
`LAPSE_CODES` (issue #97). It still carries `--origin llm` and lands
`proposed` — the same anti-fabrication discipline as every other row — but it
is filed the instant the degradation is noticed rather than batched with the
sweep's other findings, and it is adjudicated later via `devague lapse
--confirm`/`--reject`, exercised by the same human who already owns this
leg's gate — never a new gate or role.

Every finding names the **lens and surface** it came from (the
`challenge pass / <lens>: <surface>` convention in scope entries; provenance
Expand Down Expand Up @@ -201,11 +220,12 @@ blocking risks visible until resolved.
finding the agent proposes carries `--origin llm` and lands `proposed`;
only the user's `confirm` makes it real. The pass must not be able to
silently convert speculation into confirmed requirements.
- **Findings route through existing deterministic moves only.** `capture`,
`interrogate`, `question`, `park`, `devague scope`, `devague plan risk` —
nothing else. No parallel prose artifact, no new CLI verb, engine, or
state model (issue 20; issue 73's stated preference). If it didn't land in
a move, it didn't land.
- **Findings — and the pass's own reasoning self-reports — route through
existing deterministic moves only.** `capture`, `interrogate`, `question`,
`park`, `devague scope`, `devague plan risk`, `devague lapse` — nothing
else. No parallel prose artifact, no new CLI verb, engine, or state model
(issue 20; issue 73's stated preference; issue #97 for the lapse ledger).
If it didn't land in a move, it didn't land.
- **Provenance on every finding.** Name the lens and the surface it came
from. If you didn't read it, don't claim it — same bar as `/scope`.
- **Proportional, never skipped.** Lightweight is the floor, not an
Expand Down
40 changes: 30 additions & 10 deletions .claude/skills/summarize-delivery/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,18 @@ directly (if `devague` isn't on your PATH: `uv tool install devague`).
test suite, the linters, `git log` — to substantiate a claim *before* you
write it. Verification never mutates code or state. A claim you cannot
verify stays `unverified`.
6. **State delivery claims with confidence + evidence.** Each claim carries a
confidence level (`high` / `medium` / `low` / `unverified`) and at least one
**resolvable** evidence pointer, or an explicit `unverified` marker. A claim
without evidence is `unverified` — never asserted as done.
6. **State delivery claims with confidence + evidence — grounded in the lapse
ledger.** Each claim carries a confidence level (`high` / `medium` / `low` /
`unverified`) and at least one **resolvable** evidence pointer, or an
explicit `unverified` marker. Read `devague lapse --list` first (or the
`Lapse ledger evidence:` block `devague summary`'s skeleton already renders
under this section — see Method step 1): an **approved** lapse touching a
claim — an unverified grader, a below-target sample size, an assumption
standing in for a real measurement, or another of `LAPSE_CODES`
(`devague/frame.py`) — caps that claim's confidence honestly instead of
letting it default to `high`; a still-**proposed** lapse is pending, not
yet evidence, and must not be cited as if it already were. A claim without
evidence is `unverified` — never asserted as done.
7. **Name the remaining work.** What is incomplete, deferred, or newly
discovered — including any failure and its cause.
8. **Write the artifact and commit it.** Fill the eight-section template into a
Expand Down Expand Up @@ -240,6 +248,15 @@ Every Delivery Claims row carries three fields:
number** that is real, or a **test node id** that ran. "It works" is not
evidence.

`devague summary`'s skeleton already appends a `Lapse ledger evidence:` block
beneath the placeholder row whenever the frame has any filed lapses —
**approved** entries rendered as a small `Lapse | Code | What` table,
still-**proposed** ones listed as `pending approval (not yet evidence)`, and
**rejected** ones omitted entirely (`devague/render/summary_md.py`'s
`_lapse_evidence_lines`). Treat that block as the ledger's contribution to
this section — read it (or run `devague lapse --list` directly) rather than
re-deriving by memory which lapses bear on which claim.

### Drift From Plan — the entry contract

Every Drift From Plan entry names three things:
Expand Down Expand Up @@ -288,6 +305,7 @@ it documents is read-only:
|------|---------------|
| `devague summary [--pr] [--json]` | The eight-section delivery-summary skeleton (or condensed `--pr` skeleton), pre-filled verbatim from the plan's tasks, its live source frame, and the delivery (deviation) store — the primary planned-work baseline (Method step 1). |
| `devague deviate --list [--json]` | Every recorded deviation, read back by `dN` id — the source Drift From Plan and Mid-work Decisions quote. Recording or confirming a deviation is `/deviate`'s job, never this skill's. |
| `devague lapse --list [--json]` | Every filed reasoning-degradation lapse, read back by `lN` id — **approved** entries are the evidence that grounds a Delivery Claims confidence level (Method step 6); **proposed** ones are pending, not yet evidence; **rejected** ones are omitted. Filing a lapse is the agent's job at the moment the degradation is noticed (`/challenge`, `/assign-to-workforce`); confirming or rejecting one is `devague lapse --confirm`/`--reject`, exercised by the gate-owning human — never this skill's. |
| `devague plan show [--json]` | The plan's tasks, acceptance criteria, dependencies — the hand-assembly fallback when `devague summary` (or the state it needs) is unavailable. |
| `devague plan waves --json` | The wave batches + per-task `summary` / `instruction` / `acceptance_criteria` / `covers`, keyed by id — the hand-assembly fallback's verbatim planned-work baseline. |
| `devague scope --list [--json]` | Recorded scope-exploration findings, if the frame carried any. |
Expand All @@ -314,12 +332,14 @@ These are the point of the method — a delivery summary must be trustworthy.
`git log` to substantiate a claim before writing it. Verification **never**
mutates code or state. A claim you cannot verify stays `unverified`.
- **No devague state mutation.** The only devague moves this skill uses are the
read-only `summary`, `deviate --list`, `plan show`, `plan waves`,
`scope --list`, `show`, and `status` (see the table above). `deviate --list`
is read-only — recording or confirming a deviation belongs to `/deviate`,
never this skill. Never run a mutating devague command, and never run
`devague plan` inside a task worktree to "mark a task done" — that is
`/assign-to-workforce`'s boundary too (#20).
read-only `summary`, `deviate --list`, `lapse --list`, `plan show`,
`plan waves`, `scope --list`, `show`, and `status` (see the table above).
`deviate --list` and `lapse --list` are both read-only — recording or
confirming a deviation belongs to `/deviate`, and filing or adjudicating a
lapse (`lapse --confirm`/`--reject`) belongs to whoever filed it and the
gate-owning human, never this skill. Never run a mutating devague command,
and never run `devague plan` inside a task worktree to "mark a task done" —
that is `/assign-to-workforce`'s boundary too (#20).
- **Account for 100 % of plan tasks.** Every plan task appears in Actual
Delivery as delivered / partial / dropped / blocked — no silent omissions.
Both the task count and the claim-evidence coverage are checkable by
Expand Down
2 changes: 1 addition & 1 deletion .devague/current_plan
Original file line number Diff line number Diff line change
@@ -1 +1 @@
issue-backlog-sweep
reasoning-degradation-ledger
21 changes: 21 additions & 0 deletions .devague/deliveries/reasoning-degradation-ledger.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"plan_slug": "reasoning-degradation-ledger",
"schema_version": 1,
"created": "2026-07-29T18:22:32Z",
"updated": "2026-07-29T18:22:32Z",
"deviations": [
{
"id": "d1",
"what": "the split-plan --write path escapes verbatim task text before writing markdown",
"task_ref": "t5",
"reason": "the committed gate-2 artifact failed the repo's own markdownlint: cli/__init__.py in t2's instruction rendered as strong-emphasis (MD050 x2, MD037 x1). No plan task covers the split-plan script, and CI does not lint markdown, so nothing would have caught it before review. Approved by the user mid-run.",
"affects": [
"t5",
"c19"
],
"origin": "user",
"status": "approved",
"classification": "acceptable"
}
]
}
Loading
Loading