run-resource-claims: three capability gaps the contract never had — §7 attribution, recursion-guard nonce, malformed-journal fallback #354
Labels
No labels
component:adapters
component:lifecycle
component:qa
component:setup
component:shared
component:worktrees
phase/accepted
phase/backlog
phase/deployed
phase/developing
phase/integrating
phase/planning
phase/qa
phase/requirements
phase/uat
priority:critical
priority:critical
priority:high
priority:high
priority:low
priority:low
priority:medium
priority:medium
type:bug
type:chore
type:docs
type:feature
type:infra
type:tech-debt
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
jbr870/devwork-skills#354
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Spawned from the round-2 QA sweep on issue #43 (Phase Outcome PO-43-6, decisions D-PO-43-6-2/3/4).
Three findings deferred out of round 3. Round 3 was deliberately framed at one class — a normative
rule and the mechanism implementing it drifting apart — because that class had escaped two patch
rounds. These three are not that class: each is a capability the contract or its harness never
had, rather than a rule that drifted from its source. Keeping them out is what kept round 3 closable.
All three were measured, not inferred, by an executing adversarial sweep.
[code CR-49] §7's runnable output cannot be attributed to actors — 10 anonymous claim lines, 9 byte-identical, with the actor and timestamp keys deleted by its own trap.
Severity: medium
Driver-authorised sweep ran the shipped recipe verbatim against the real journal: it prints claims JSON only, no actor names or windows, and the trap removes $SCAN which held the actor:/timestamp: lines needed to pair claim to actor to window. To feed any pair into §4 — which is what steps 3 and 4 then ask for — the reader must redo the extraction from the journal by hand. A gap with a workaround, but it is exactly the manual re-derivation that 'answer it from the record' exists to remove.
Proposed action: Emit actor and window alongside each claim, or keep the scan available to the steps that need it.
[code CR-50] An exported RRC_INNER_RUN makes an outer run silently pass three guarded scenarios — a measured false-green vector with an exotic precondition.
Severity: low
The residual the fix stage carried forward, now measured in both halves on scratch copies. (1) RRC_INNER_RUN=1 exported into an OUTER run: three SKIP (LOUD) lines, then PASS for all three guarded scenarios, suite rc=0, skip_count=3. (2) The same leak plus the CR-4 defect re-introduced: rc=0 — a false green; the identical masked tree in a clean environment reds, both cr4 scenarios FAIL, rc=1. Judged Low because it needs the operator or CI to export the variable, and the SKIP lines and skip_count are visible in the output. Cheap hardening exists: the outer run sets the guard to a per-run nonce and the inner guard compares equality, so an inherited stale value cannot match.
Proposed action: Nonce the recursion guard so an inherited value cannot satisfy it.
[code CR-51] CR-40's fixture fallback keys on existence only, so a present-but-malformed real journal reds the required suite instead of falling back.
Severity: low
Measured: scratch copy with the real journal replaced by garbage, scenario_cr1 FAILs and the suite exits 1, with no fixture fallback. The failure is loud and its diagnostic is correct, and only this feature's own local .devwork file can produce the state, so the blast radius is one machine. But CR-40's whole point was that a required Verification command must not red on a state the developer did not cause.
Proposed action: Fall back when the real journal is unusable, not only when it is absent.
Deferral rationales are on #43 as
decision-resolution:v1comments 2059, 2060 and 2061.This issue carries no PREQ yet. Run
/dev:requirements --issue={new-number}before planning.Folded in from the round-3 QA sweep on issue #43 (Phase Outcome PO-43-7).
Measured by an executing adversarial reviewer, not inferred. Deferral rationales are on #43 as
decision-resolution:v1comments.[code CR-59] The reference journal violates journal-template's UTC rule in every entry written after that rule shipped — no mechanism checks it.
Severity: medium
The CR-8 fix states the stamp IS UTC, 'whatever zone the operator is in', and shipped in round 1's fix commit on 2026-08-25. All eight entries appended on 2026-08-26 carry +02:00, because the driver's dispatch/release helpers called
date -Isecondsrather thandate -u -Iseconds. No wrong answer today — the offset is uniform, so ordering and intervals are unaffected — but this is the third instance of 'a rule with no enforcement path is a rule that will be missed', after CR-26 (a suite no gate invoked) and CR-36 (a required field the writing helper had no parameter for). DRIVER HAS ALREADY FIXED THE HELPERS and appended a correction entry using the very supersession shape CR-52 added; what remains deferrable is the absence of a mechanical check.Proposed action: Add a mechanical check that journal stamps are UTC.
Folded in from the round-5 QA sweep on issue #43 (Phase Outcome PO-43-9, finding CR-74, decision D-PO-43-9-3).
§4's sum test has no executable form, and it decides the common case
CR-74 was split at resolution. Its wording half — step 4 restating the arithmetic as "total …
plus the pool", which includes the pool in the total and therefore always oversubscribes — is being
fixed in round 6. This half is deferred here.
The gap. §4's bold rule and §7 step 4's budget branch both route the common realistic outcome
to the sum test: every §3-conformant claim pair names
workers, so the filter's output isbudget-only, and the disposition then depends entirely on a test that exists nowhere in executable
form. Grep confirms no
add,reduceortonumberanywhere in the contract beyond port expansionand the round-5 padding wrapper. The only implementation is a repo-local helper inside
scripts/test-run-resource-claims.sh, and even that is pairwise (overrun plus one peer) ratherthan the global total §4 specifies.
Why it is a capability rather than a drift. A global sum needs a source of truth for which
actors are currently open — dispatched and not yet released. The §7 recipe has no such view: it
scans a journal for claim lines and never reconstructs the open set. That is new mechanism, not a
missing line, which is why it belongs with this issue's set rather than inside a round scoped at a
parsing bug.
Why it matters. This is the last unenforced rule in the contract, and "a rule with no enforcement
path is a rule that will be missed" is the most repeated finding across five QA rounds on #43 —
CR-26 (a suite no gate invoked), CR-36 (a required field the writing helper had no parameter for),
CR-59 (a UTC rule violated the day after it shipped), CR-71 (an enforcement check that could not
parse its own repo's records).