QA: intra-run lane-ownership primitive, plus a void-vs-flaky triage rule #43
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#43
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?
PREQ: Intra-run resource ownership — a claim primitive, plus a void-vs-flaky triage rule
Created: 2026-08-25
Problem
A run driver (the orchestrator model of an autonomous run, or a human driving the phases) dispatches
several actors that each execute a test suite — a QA validator, a fixer agent re-running the suite it
just patched,
/dev:develop's work units in a wave. Inside one worktree those actors share the sametest database, the same ports and the same runner concurrency. Nothing in the suite says who owns
what for the duration of a run, so ownership gets stated ad hoc, per brief, or not at all.
Two failures follow, and both have been measured twice:
pnpm testconcurrently with a fixer agent'spnpm test, both againstverity_slot4_test. Three tests failed in a package the fix had nevertouched. The driver spent 18 minutes proving the failures were database contention rather than a
defect, then re-ran as sole owner: exit 0. Its own words — "I caused a false alarm."
verity-slot3-191, the driver violated its ownserialization rule three times during a five-hour phantom-instability hunt. Its conclusion:
"a contended run doesn't give flaky results, it gives void ones, and I treated void results as
evidence three times."
The same #230 run also shows the correct handling — every later stage hand-carried an explicit
ownership statement ("the re-validator has vitest and
verity_slot4_test; I have Playwright,_e2eand port 4082"). It worked. The defect is not that those statements were prose; it is that
writing one was optional, unstructured, and re-invented per brief, with nothing comparing two of
them before dispatch.
Users:
the fix loop,
/dev:developwaves). It needs an unambiguous statement of what each agent owns, andit needs to notice before dispatch that two briefs would collide.
reporting; and the human operator reading the run record afterwards, who currently cannot tell a
contended run from a real failure.
Current state: The suite has ownership rules for the cross-slot case only —
qa-playbook.mdpreconditions 4–5 and
slot-isolation.md§7.2 (read ports and database names from<worktree>/.slot.env,assert end-to-end that the server answering is yours, never guess a port). Those stop slot A from
testing slot B's build. They say nothing about two actors inside the same slot, which is the case
every wave and every fix round produces.
/dev:develop§3.1's parallelism rule covers only the gitindex ("work units must not share files"; serialize on a single worktree) — a work unit's suite and its
database are not mentioned. And the one void-vs-flaky statement that exists is scoped to a server that
died (EADDRINUSE / crash), not to a suite that merely shared a database and returned a plausible
red.
Proposed Solution
Three parts, stated once and referenced by every consumer.
1. Serialize by default. Two actors do not run test suites concurrently in the same worktree unless
each holds a claim (part 2) and their claims do not overlap. This alone cures both measured incidents;
the claim is what buys the concurrency back.
2. A resource-claim primitive. The suite names a small vocabulary of the resources concurrent actors
contend on — database identities, ports, runner concurrency (workers/threads), and shared external
sandboxes — and a project may add named resources of its own without a new mandatory declaration
block. Every brief that dispatches an actor which will run a suite carries a structural claim: a
named field with a fixed set of keys naming the resources that actor owns exclusively for the duration
of its work, rather than a sentence composed from scratch. Before dispatching concurrently, the driver
compares claims; two actors whose claims name the same resource are not dispatched together — the
driver either gives them distinct resources or serializes them, and the run record says which it did.
The claim is declared and honoured, not locked. There is no new helper, no on-disk registry and no
claim/release verbs — the failure being fixed is that ownership was optional and unstructured, not that
a lock was missing, and a lock would duplicate state the slot registry already owns. A claim ends when
the driver has the actor's report, or when the driver has positively established that the actor is
gone (a real liveness check). Silence alone never releases a claim.
3. A void-vs-flaky triage rule. A result is void — not pass, not fail, not flaky — when its
actor's execution overlapped in time with another actor holding a claim on the same resource. It is
re-run under sole ownership before anything is classified, and a contended run is never recorded as
evidence in either direction. "Flaky" survives as a real classification for an actor that did own its
resources, so the rule narrows what may be called flaky without abolishing the category.
Scope: Standard — serialize-by-default, the structural claim, and the triage rule, applied to both
QA and
/dev:develop. Not the enforced-lock version (recorded under Out of Scope, with the reasoning).User Stories
concurrency that agent owns, so that I stop composing that sentence from scratch and stop omitting it.
so that I serialize them before paying for a false alarm rather than diagnosing one for 18 minutes
afterwards.
not either stall the run forever or get its resources handed away while it is still writing to them.
neither report a defect that isn't there nor fix code that was never broken.
so that I can trust a finding count without re-deriving the environment it was measured in.
Acceptance Criteria
then they are run one after the other, not concurrently.
it carries the actor's exclusively-owned resources in a named field with a fixed set of keys —
not a free-prose sentence — and this holds even when only one such actor is dispatched.
claim, and its absence is not treated as an empty claim over everything.
concurrently, then it does not; it either gives them distinct resources or serializes them, and
the run record carries an entry saying which it did.
claim still resolves from the suite's own resource vocabulary; the absence of a project
declaration is not a readiness gap and does not block the run.
then those resources are claimable alongside the suite's vocabulary, using a declaration surface
the project already has — no new mandatory block.
then it may do so only after positively establishing the actor is gone; elapsed silence alone
never releases a claim.
on the same resource, when the result is triaged, then it is classified void and re-run under
sole ownership before any pass / fail / flaky classification is made.
record's dispatch entries answer it — the question is decided from what the driver recorded, not
reconstructed by inference.
round closes, then the result stays void and is raised as a structural finding — it is never
converted into a pass, a fail, or a flake by exhaustion.
it; the record states that the run was contended and was re-run.
that finding is withdrawn or re-derived from an isolated re-run, and the record says so.
then
flakyremains an available classification — the void rule narrows the flaky categorywithout abolishing it.
/dev:developdispatches a wave of work units that each run tests, when their briefs arebuilt, then the same rules apply to them — stated once in one document and referenced by both the
QA playbook and
/dev:develop, not restated in either.the identity surface (
slot-isolation.md§7.2) and the new text defers to it rather thanrestating how they are resolved; the claim partitions ownership within the slot.
test-plan:v1scenario lanes, when theyread either document, then the two concepts carry different names and each document names the
other, so neither is mistaken for it.
Out of Scope
Considered and declined at requirements time: new helper-tier code and a second state surface
overlapping the slot registry, to defend against an actor that ignores its own brief — which is not
the failure we measured.
/dev:develop's dispatch ordering — issue #39 (dispatch work units by declared dependency) staysa separate issue. #43 gives it the ownership rules to build on; it does not change how units are
ordered or scheduled.
problem; the dispatch entries this feature writes to the run record are what a resumed driver would
read, and that is as far as this feature goes.
terminal. The triage rule keys on claims; making it key on observed use would require the enforcement
layer this feature declines.
slot-isolation.mdowns the recipe, the port scheme andthe database identities. This feature consumes them; it does not extend them.
parallel_dev:recipe). Adjacent and itconstrains us — hence the "no new mandatory declaration" criterion — but fixing the recipe schema is
not this feature.
Dependencies
_shared/procedures/slot-isolation.md§7.2 and_shared/procedures/qa-playbook.mdpreconditions 4–5 (existing cross-slot ownership); must not contradict either.
_shared/procedures/qa-playbook.md,_shared/procedures/fix-workflow.md,_shared/procedures/validate-workflow.mdand/dev:develop§3.1.wait-discipline.mdand issues #44 / #58 (an idlesignal is not completion; verify DONE as a claim) — it must not contradict them.
feature governs.
scope adds text, brief shape and record entries; no new helper.
Timeline
Notes
decision. The vocabulary must not collide with
test-plan:v1scenario lanes.project declaration (avoids repeating the #62 readiness trap).
/dev:develop, so #39 inherits them.claim is what buys concurrency back, which is precisely what #39 needs.
positive death check, never on elapsed silence.
/dev:technical-plan: the exact token for the primitive, the key set the claimfield carries, which document owns the canonical statement that the QA playbook and
/dev:developboth reference, and the shape of the run-record dispatch entry the triage rule reads. Three more the
panel named, each a design question the acceptance criteria hold either way:
the runner. Two implementers would model "owns the runner concurrency" differently.
Provisioning new ones is out of scope, so the answer is presumably "identities the slot already
owns" (its declared derived databases, its port block) — but the PREQ does not say it, and the
difference between partitioning what exists and creating a second database is a real fork.
rate-limited third-party test account is the shape intended.
Origin — the retrospective proposal this PREQ was drafted from
Filed 2026-08-14 as rank 6 of 13 in #37 (est. saving ~20m plus the false-alarm class); promotes
watchlist items RC-W17 and RC-W18 on a second independent sighting.
Test Plan: qa-intra-run-lane-ownership
Prerequisites
The deliverable is skill text plus the brief and run-record shapes it mandates, so most scenarios are
executed by reading the shipped suite as a newcomer would and by driving a real run and watching what
the driver does. No servers or credentials are needed to judge them.
reader has to be told about).
actors to overlap.
additional contendable resources.
single-worktree project.
Required Test Data
/dev:developto want a wave.while another actor could also be running one.
case.
copies run against one database. The #230 shape: three failures in a package the change never
touched.
Test Scenarios
Scenario 1: Two suite-running actors with no claims are run one at a time
Acceptance criterion: "Given two actors that would run test suites in the same worktree, when
neither holds a claim, then they are run one after the other, not concurrently."
neither a claim.
Expected outcome: No two unclaimed suites are in flight at once, and the reader can see the decision
rather than infer it from timing.
Scenario 2: A dispatched brief carries its ownership as a named field
Acceptance criterion: "…it carries the actor's exclusively-owned resources in a named field with a
fixed set of keys — not a free-prose sentence — and this holds even when only one such actor is
dispatched."
point at "the database it owns" without parsing a sentence.
Expected outcome: Ownership is readable the same way in every brief, including a solo one.
Scenario 3: A brief that runs no suite carries no claim
Acceptance criterion: "Given a brief for an actor that runs no test suite… it carries no claim, and
its absence is not treated as an empty claim over everything."
that empty brief.
Expected outcome: Silence in a non-executing brief means "not applicable", never "claims everything".
Scenario 4: Two briefs claiming the same thing are not dispatched together
Acceptance criterion: "…it does not; it either gives them distinct resources or serializes them, and
the run record carries an entry saying which it did."
resource that collided.
Expected outcome: The collision is visible in the record afterwards, not only in the timing.
Scenario 5: A project that declares nothing still gets claims, and is not blocked
Acceptance criterion: "…the claim still resolves from the suite's own resource vocabulary; the
absence of a project declaration is not a readiness gap and does not block the run."
Expected outcome: A project that has declared nothing works out of the box and is never nagged.
Scenario 6: A project's own named resources are claimable
Acceptance criterion: "…those resources are claimable alongside the suite's vocabulary, using a
declaration surface the project already has — no new mandatory block."
two actors both needing it.
Expected outcome: Project-specific resources are first-class in a claim without a new obligation.
Scenario 7: A silent actor's resources are not handed away on silence alone
Acceptance criterion: "…it may do so only after positively establishing the actor is gone; elapsed
silence alone never releases a claim."
says what it checked.
Expected outcome: Slowness is never mistaken for death; death is established, not assumed.
Scenario 8: An overlapped result is void and re-run before it is classified
Acceptance criterion: "…it is classified void and re-run under sole ownership before any
pass / fail / flaky classification is made."
time, so one comes back with the #230-shaped failures.
Expected outcome: No verdict is ever pronounced on the contended result.
Scenario 9: "Was it contended?" is answered from the record
Acceptance criterion: "…the run record's dispatch entries answer it — the question is decided from
what the driver recorded, not reconstructed by inference."
when — without guessing from timestamps in logs or asking the driver.
Expected outcome: Contention is a recorded fact, not a reconstruction.
Scenario 10: When isolation is impossible, the result stays void and is raised
Acceptance criterion: "…the result stays void and is raised as a structural finding — it is never
converted into a pass, a fail, or a flake by exhaustion."
nothing can get sole use of.
Expected outcome: Unresolvable contention becomes a visible, decidable problem instead of quietly
becoming a verdict.
Scenario 11: Nothing cites a void result
Acceptance criterion: "…no finding, no fix and no verdict cites it; the record states that the run
was contended and was re-run."
Expected outcome: The void result exists in the history as a discarded measurement, and nowhere else.
Scenario 12: Contention found late withdraws what it fed
Acceptance criterion: "…that finding is withdrawn or re-derived from an isolated re-run, and the
record says so."
contended.
disappearance.
Expected outcome: Late discovery repairs the record rather than leaving a finding standing on void
evidence.
Scenario 13: A genuinely flaky test is still called flaky
Acceptance criterion: "…
flakyremains an available classification — the void rule narrows the flakycategory without abolishing it."
Expected outcome: Exclusive ownership is what makes flakiness a real verdict again.
Scenario 14: A development wave obeys the same rules
Acceptance criterion: "…the same rules apply to them — stated once in one document and referenced by
both the QA playbook and
/dev:develop, not restated in either."/dev:developon a feature whose wave has two work units that both run tests.concurrently.
/dev:develop.Expected outcome: One rule, two consumers, no second copy.
Scenario 15: In a slot, the claim uses the slot's own identities
Acceptance criterion: "…the values come from the identity surface… and the new text defers to it
rather than restating how they are resolved."
re-explain or contradict it.
Expected outcome: The claim divides what the slot already owns; it does not invent a second way to
decide what the slot owns.
Scenario 16: A newcomer does not confuse this with scenario lanes
Acceptance criterion: "…the two concepts carry different names and each document names the other, so
neither is mistaken for it."
Expected outcome: A reader meeting both on the same day can tell them apart without being told.
Scenario 17: Edge cases
Verify: everything serializes and the run still completes; nothing deadlocks waiting for a claim.
Verify: the rules add no ceremony that blocks or slows it beyond carrying its claim.
Verify: the common resource is treated as a collision; the distinct ones do not force extra
serialization beyond it.
Verify: the second dispatch is allowed, and the record shows the first claim ended at the report.
Notes
declaration says validation is the shell test harness plus dogfooding, and these scenarios are written
for that.
chosen project, that is a prerequisite gap to solve before QA, not a reason to weaken the scenario.
design decisions and are settled in
/dev:technical-plan.PREQ amendment — clarification only, no acceptance criterion changed
When: 2026-08-25, immediately after PO-43-1.
What changed: the Notes section's Open questions for
/dev:technical-planlist only. Threequestions the requirements panel's ambiguity lens named were added by name:
partitioning identities the slot already owns, versus creating new ones (provisioning is out of
scope, but the PREQ did not say which);
intended).
Why it is recorded rather than silently edited:
test-plan.v1.md§Freshness makes a recordedPREQ amendment the staleness signal for the validation cases, precisely so no mtime has to be trusted.
Effect on the validation cases: none. No user story, no acceptance criterion and no Out of Scope
item moved — the definition of done is byte-identical. All three additions are design questions whose
answers the existing criteria hold either way, parked where
/dev:technical-planwill read them. Thetest-plan:v1deliverable (comment 1301, 17 scenarios / 16 acceptance criteria) is therefore current,not stale, and should not be regenerated on account of this amendment.
Provenance of the three items: the Tier-3 requirements panel ran five lenses; their hand-back
messages did not reach the driver's inbox and the reports were first recovered from the subagent
transcripts (recorded as a finding on PO-43-1). The messages were delivered late, after PO-43-1 was
posted; re-reading them against the amended PREQ confirmed every other concern was already settled and
surfaced these three as the only residue.
Software Requirements: qa-intra-run-lane-ownership
Context
A run driver dispatches several actors that each execute a test suite — a QA validator, a fixer agent,
/dev:develop's work units in a wave. Inside one worktree they share the test database, ports and runnerconcurrency, and nothing in the suite states who owns what for the duration of a run. The measured cost is
18 minutes spent proving three "failures" were database contention (#230) and a five-hour
phantom-instability hunt in which contended results were treated as evidence three times
(
verity-slot3-191). The constraint from requirements: declared and honoured, not locked — no newhelper, no on-disk registry, and no new mandatory project declaration.
Approaches Considered
Approach A: Extend
worktree-discipline.mdSummary: put the intra-worktree rules in the file that already owns "parallel agents in one repository
without corrupting each other" and already carries the void-vs-flaky sentence.
Pros: no new document; the neighbouring rules (one writer per worktree, the environment-ownership
check) are the same family; every consumer already reads it.
Cons: that file's subject is linked worktrees and slots. The claim applies with no worktrees at all —
two agents in a plain main checkout contend on one database. Widening it to fit would misname the file.
Effort: Low
Approach B: A new canonical procedure document
Summary:
_shared/procedures/run-resource-claims.mdowns the vocabulary, the claim shape,serialize-by-default, the claim lifecycle, the dispatch record and the void-vs-flaky taxonomy. Every
consumer references it and restates nothing.
Pros: satisfies AC-14 ("stated once in one document and referenced by both") literally; scope is honest
(any concurrent actors, worktree or not); follows the suite's established canonical-doc pattern
(
red-markers.md,observability-policy.md).Cons: a fifth cross-cutting procedure document; a reference edit in each consumer.
Effort: Medium
Approach C: Fold into
slot-isolation.mdSummary: claims live next to the identities they partition.
Pros: one place for "who owns which database".
Cons: slot-isolation is the provisioning authority and mandates port + database axes — issue #62
records that a services-free project cannot declare a valid recipe at all. Putting a rule every project
needs inside the one document a services-free project cannot satisfy repeats exactly the trap AC-5 forbids.
Effort: Medium
Decision
Selected: Approach B.
Rationale: AC-14 requires a single statement referenced by both the QA playbook and
/dev:develop;only B provides one without widening a document past its own name (A) or inheriting #62's declaration trap
(C). The suite already uses this shape for cross-cutting rules, so it adds a pattern instance rather than a
pattern.
Architecture
Component Overview
Dependency direction is one-way and must stay so: claims consume identities; provisioning owns
them.
run-resource-claims.mdreads values fromslot-isolation.md§7.2 (slotted) ordev-server-lifecycle.md(unslotted) and never allocates, creates or drops anything. A future edit thatinverts this — letting a claim provision a resource — is the architectural regression to refuse.
Data Flow
claims:block into the brief.two concurrently: it reassigns from identities the project already declares, or serializes them.
DISPATCHentry per dispatched actor to the driver-side journal, recording thepost-remedy claim — what was actually dispatched, not what was requested.
wait-discipline.md§1a), itappends a
RELEASEentry. Journals are append-only; no entry is ever edited in place.overlap — never by trusting one entry's
concurrent_with, which is dispatch-time only and cannot see alater-dispatched actor.
The resource vocabulary
Four suite-owned classes. A project may name additional resources through declaration surfaces it already
has; no new mandatory block (the #62 guard). A project that declares nothing gets these four and is
never reported as having a readiness gap.
database.slot.envSLOT_DB/SLOT_DB_<SUFFIX>(slot-isolation §7.2). Unslotted: the project's declared test database namesportdev-server-lifecycle.mdderivationworkersexternalOverlap — two different tests, because
workersis not a setThree reviewers independently flagged that "intersect the claims" is undefined for a scalar count. The rule
is therefore stated per kind:
database,port,external, and any project-named class): overlap = non-empty setintersection. A port range is written
"4080-4089"and expanded to integers before intersecting, so thetest is one representation, computable with
jqalone.workers(budget): overlap = sum of concurrently-claimed workers exceeds the project's runnerpool.
workers: 4andworkers: 4collide on an 8-worker box only if something else also claims;they do not collide merely by being equal.
Present-but-empty vs absent — the distinction that stops the original bug reappearing
The UX lens caught that "omitted means owns nothing" reproduces the very defect being fixed: two briefs
that both omit
database:still both hit the project default, pass an intersection test, and getdispatched together. So:
[]claims:block absentA brief for an actor that runs no suite carries no
claims:block, and that absence isnot-applicable, not a claim over everything (AC-3). The two absences are distinguished by whether the
actor runs a suite, which the driver knows because it is the thing dispatching it.
The dispatch record — two append-only journal entry types
journal-template.mdis the single source of truth for entry types, so this adds entries rather than a newtyped forge comment. The driver's journal is
$FEATURE_FOLDER/dispatch-journal.md— bound as$JOURNAL_FILEfor the driver role, appended across every phase of the feature. Naming it is what givesAC-9's "run record" a defined home rather than an implication.
RELEASEis a separate entry precisely becausereleased:is unknowable when the DISPATCH entry iswritten, and
journal-template.md's contract is append-as-you-work. Nothing mutates a past entry.Void — one taxonomy, one home
run-resource-claims.mdowns the void definition in full. Two causes, one classification:qa-playbook.mdprecond. 5,worktree-discipline.md§env-ownershipA result is void ⇒ re-run under sole ownership before any pass / fail / flaky classification; nothing may
cite it; if isolation is unattainable it stays void and becomes a structural finding (per
validate-workflow.mdStep 3, so it reaches a scope-disposition decision) rather than a verdict byexhaustion.
flakyremains available for an actor that did own its claims exclusively. An actor whosereport shows it used a resource beyond its claim (
overran_claim: true) voids its own result — thedeclared-and-honoured model has no enforcement, so the detection is the report, and the consequence is
stated rather than left to judgement.
Late discovery
When contention is established after a finding has already cited a result, the finding is withdrawn or
re-derived from an isolated re-run.
qa-report:v1is mutable / latest-wins, so the mechanism alreadyexists: re-post the domain's report with the finding withdrawn and the reason named.
validate-workflow.mdgains the rule; no new record kind.
Key Decisions
run-resource-claims.mdworkerssemantics$FEATURE_FOLDER/dispatch-journal.md, driver-ownedTechnical Risks
derived_suffixes(slot-isolation §1), which already lets a slot own several database identities. Widening a project's pool is a project declaration change, not this feature. Surfaced as a finding so it reaches a decision alongside #39waited_mson DISPATCH entries makes the cost measurable from the record instead of guessed; direction of error chosen deliberately (slow-and-correct over fast-and-wrong)test-plan:v1scenario lanesExpert Review
Reviewers
append-only contract; the journal had no named home; void semantics risked living in two places.
workerscannot be intersected;released:unknowable at write time;[]vsomitted spelled two ways; port-range representation unpinned; recorded claims should be post-remedy.
database — #39 degrades to fully serial by construction, not as a fallback;
workersintersectionundefined.
workers;released:conflicts with the write model; triage needs a scan recipe rather than trustingone entry's
concurrent_with.Changes Made
DISPATCH+RELEASEentries; nothing is mutated in place.$FEATURE_FOLDER/dispatch-journal.md.workersbudget.[]and from awholly absent block on a non-suite brief.
run-resource-claims.mdthe single home of the void taxonomy; the two existing texts point at it."4080-4089", expanded before intersecting) sojqalone suffices.waited_msso over-serialization is measurable.(
qa-report:v1latest-wins).Noted (not actioned)
rule but it is
wait-discipline.md's subject, not this document's; noted in Technical Risks instead ofadding a second waiting protocol.
carries
waited_ms, which makes the measurement possible after the fact; blocking this feature on abenchmark would gate a correctness fix behind a performance study.
problem, and the DISPATCH/RELEASE entries are what a resumed driver would read.
Acceptance Criteria
codedomain: the serialize-default rule is stated inrun-resource-claims.mdand referenced by both consumers; dogfood run observationcodedomain inspection of the brief shape + the spawn-prompt rule inqa-playbook.mdanddevelop/SKILL.mdcodedomain: the present-but-empty vs absent table is normative in the docremedyis a required key of theDISPATCHentry injournal-template.md; dogfood run observationscripts/lint-conventions.shgains a check that no new required item is added toreadiness-check.mdand thatrun-resource-claims.mddeclares the no-declaration defaultcodedomain inspection; the same lint check as AC-5 asserts no new mandatory declaration blockcodedomain: the doc defers towait-discipline.md§1a and states the never-on-timeout rule;RELEASE.ended_byenumerates onlyreport/death-checkcodedomain: the void definition and its ordering are normative; dogfood observation on a contention-sensitive suiteDISPATCH/RELEASEentry types exist injournal-template.mdwith the named journal file; a reader executes the triage scan recipe against a real run's journalcodedomain: the rule routes tovalidate-workflow.mdStep 3's structural classificationcodedomain inspection ofvalidate-workflow.md+fix-workflow.mdeditscodedomain: the latest-winsqa-report:v1withdrawal rule is stated invalidate-workflow.mdflakyremains available for an actor that owned its claims exclusivelycodedomain: the void taxonomy explicitly preserves the flaky category/dev:develop, not restatedscripts/lint-conventions.shgains a check that the normative sentences appear in exactly one file and that both consumers cite it by pathslot-isolation.md§7.2 rather than restating ittest-plan:v1scenario lanes, and each document names the otherrun-resource-claims.mdandtest-plan.v1.mdeach contain a cross-reference to the otherMechanical rows are load-bearing here. AC-14 and AC-15 are exactly the "every call site routes through
helper X" shape: the failure mode is one consumer quietly restating the rule instead of referencing it,
which reads correct in isolation and drifts silently. A spot-check of prose does not catch it; a check that
the normative sentence exists in exactly one file does.
Implementation Scope
Areas
plugin/skills/_shared/procedures/run-resource-claims.mdplugin/skills/_shared/procedures/journal-template.mdplugin/skills/_shared/procedures/qa-playbook.mdplugin/skills/_shared/procedures/validate-workflow.mdplugin/skills/_shared/procedures/fix-workflow.mdplugin/skills/develop/SKILL.md§3.1plugin/skills/_shared/procedures/worktree-discipline.mdplugin/skills/_shared/schemas/test-plan.v1.mdscripts/lint-conventions.shFile Boundaries
Every file above is touched by exactly one work unit — no unit shares a file with another, which is the
constraint that makes the wave safe. Natural split:
journal-template.mdentry types (independent of WU-1's prose; the shapes are specified here).qa-playbook,validate-workflow,fix-workflow).develop/SKILL.md+worktree-discipline.md+test-plan.v1.mdcross-references.scripts/lint-conventions.shmechanical checks.Dependencies & Sequencing
WU-1 and WU-2 are independent of each other and of everything else — they can run in parallel. WU-3 and
WU-4 only cite WU-1, and the path and section names are fixed by this SREQ, so they need WU-1's content
only at review time, not at authoring time. WU-5 must land last: its checks assert the shape the other
four produce, and a check written before its target exists fails for the wrong reason.
Wave 1: WU-1, WU-2, WU-3, WU-4 (parallel — disjoint files).
Wave 2: WU-5.
This feature's own development is subject to the rule it describes, which is the cheapest possible
dogfood: the wave above runs
bash scripts/lint-conventions.sh, so each unit's brief should carry a claimeven though the only contended resource here is the gate itself.
Constraints & Non-Goals
Constraints:
sh/zsh/bash); helper tier is bash ≥ 3.2 +jq+git. The claimblock and journal entries must be readable with
jqand flat-YAML-safe: scalars and flat string listsonly, no anchors or aliases (the same parseability constraint
parallel_dev:carries).scripts/lint-conventions.shis repo-local and fence-aware; new checks must reuse its existing fencetracker rather than line-regexing.
Non-goals (do NOT build):
/dev:develop's dispatch ordering (#39).parallel_dev:recipe schema (#62).Test Plan: qa-intra-run-lane-ownership
Prerequisites
The deliverable is skill text plus the brief and run-record shapes it mandates, so most scenarios are
executed by reading the shipped suite as a newcomer would and by driving a real run and watching what
the driver does. No servers or credentials are needed to judge them.
reader has to be told about).
actors to overlap.
additional contendable resources.
single-worktree project.
Required Test Data
/dev:developto want a wave.while another actor could also be running one.
case.
copies run against one database. The #230 shape: three failures in a package the change never
touched.
Test Scenarios
Scenario 1: Two suite-running actors with no claims are run one at a time
Acceptance criterion: "Given two actors that would run test suites in the same worktree, when
neither holds a claim, then they are run one after the other, not concurrently."
Lane: human-uat — owner: the operator, dogfooding a run against a real project (this repo declares
e2e,a11yandsecurity-browsernot applicable, so no automated browser lane exists here)neither a claim.
Expected outcome: No two unclaimed suites are in flight at once, and the reader can see the decision
rather than infer it from timing.
Scenario 2: A dispatched brief carries its ownership as a named field
Acceptance criterion: "…it carries the actor's exclusively-owned resources in a named field with a
fixed set of keys — not a free-prose sentence — and this holds even when only one such actor is
dispatched."
Lane: human-uat — owner: the operator, dogfooding a run against a real project (this repo declares
e2e,a11yandsecurity-browsernot applicable, so no automated browser lane exists here)point at "the database it owns" without parsing a sentence.
Expected outcome: Ownership is readable the same way in every brief, including a solo one.
Scenario 3: A brief that runs no suite carries no claim
Acceptance criterion: "Given a brief for an actor that runs no test suite… it carries no claim, and
its absence is not treated as an empty claim over everything."
Lane: human-uat — owner: the operator, dogfooding a run against a real project (this repo declares
e2e,a11yandsecurity-browsernot applicable, so no automated browser lane exists here)that empty brief.
Expected outcome: Silence in a non-executing brief means "not applicable", never "claims everything".
Scenario 4: Two briefs claiming the same thing are not dispatched together
Acceptance criterion: "…it does not; it either gives them distinct resources or serializes them, and
the run record carries an entry saying which it did."
Lane: human-uat — owner: the operator, dogfooding a run against a real project (this repo declares
e2e,a11yandsecurity-browsernot applicable, so no automated browser lane exists here)resource that collided.
Expected outcome: The collision is visible in the record afterwards, not only in the timing.
Scenario 5: A project that declares nothing still gets claims, and is not blocked
Acceptance criterion: "…the claim still resolves from the suite's own resource vocabulary; the
absence of a project declaration is not a readiness gap and does not block the run."
Lane: integration-covered —
scripts/lint-conventions.sh(the mechanical check this SREQ adds); the run-observation steps are corroboration performed by the human-uat owner in the same sittingExpected outcome: A project that has declared nothing works out of the box and is never nagged.
Scenario 6: A project's own named resources are claimable
Acceptance criterion: "…those resources are claimable alongside the suite's vocabulary, using a
declaration surface the project already has — no new mandatory block."
Lane: integration-covered —
scripts/lint-conventions.sh(the mechanical check this SREQ adds); the run-observation steps are corroboration performed by the human-uat owner in the same sittingtwo actors both needing it.
Expected outcome: Project-specific resources are first-class in a claim without a new obligation.
Scenario 7: A silent actor's resources are not handed away on silence alone
Acceptance criterion: "…it may do so only after positively establishing the actor is gone; elapsed
silence alone never releases a claim."
Lane: human-uat — owner: the operator, dogfooding a run against a real project (this repo declares
e2e,a11yandsecurity-browsernot applicable, so no automated browser lane exists here)says what it checked.
Expected outcome: Slowness is never mistaken for death; death is established, not assumed.
Scenario 8: An overlapped result is void and re-run before it is classified
Acceptance criterion: "…it is classified void and re-run under sole ownership before any
pass / fail / flaky classification is made."
Lane: human-uat — owner: the operator, dogfooding a run against a real project (this repo declares
e2e,a11yandsecurity-browsernot applicable, so no automated browser lane exists here)time, so one comes back with the #230-shaped failures.
Expected outcome: No verdict is ever pronounced on the contended result.
Scenario 9: "Was it contended?" is answered from the record
Acceptance criterion: "…the run record's dispatch entries answer it — the question is decided from
what the driver recorded, not reconstructed by inference."
Lane: human-uat — owner: the operator, dogfooding a run against a real project (this repo declares
e2e,a11yandsecurity-browsernot applicable, so no automated browser lane exists here)when — without guessing from timestamps in logs or asking the driver.
Expected outcome: Contention is a recorded fact, not a reconstruction.
Scenario 10: When isolation is impossible, the result stays void and is raised
Acceptance criterion: "…the result stays void and is raised as a structural finding — it is never
converted into a pass, a fail, or a flake by exhaustion."
Lane: human-uat — owner: the operator, dogfooding a run against a real project (this repo declares
e2e,a11yandsecurity-browsernot applicable, so no automated browser lane exists here)nothing can get sole use of.
Expected outcome: Unresolvable contention becomes a visible, decidable problem instead of quietly
becoming a verdict.
Scenario 11: Nothing cites a void result
Acceptance criterion: "…no finding, no fix and no verdict cites it; the record states that the run
was contended and was re-run."
Lane: human-uat — owner: the operator, dogfooding a run against a real project (this repo declares
e2e,a11yandsecurity-browsernot applicable, so no automated browser lane exists here)Expected outcome: The void result exists in the history as a discarded measurement, and nowhere else.
Scenario 12: Contention found late withdraws what it fed
Acceptance criterion: "…that finding is withdrawn or re-derived from an isolated re-run, and the
record says so."
Lane: human-uat — owner: the operator, dogfooding a run against a real project (this repo declares
e2e,a11yandsecurity-browsernot applicable, so no automated browser lane exists here)contended.
disappearance.
Expected outcome: Late discovery repairs the record rather than leaving a finding standing on void
evidence.
Scenario 13: A genuinely flaky test is still called flaky
Acceptance criterion: "…
flakyremains an available classification — the void rule narrows the flakycategory without abolishing it."
Lane: human-uat — owner: the operator, dogfooding a run against a real project (this repo declares
e2e,a11yandsecurity-browsernot applicable, so no automated browser lane exists here)Expected outcome: Exclusive ownership is what makes flakiness a real verdict again.
Scenario 14: A development wave obeys the same rules
Acceptance criterion: "…the same rules apply to them — stated once in one document and referenced by
both the QA playbook and
/dev:develop, not restated in either."Lane: integration-covered —
scripts/lint-conventions.sh(the mechanical check this SREQ adds); the run-observation steps are corroboration performed by the human-uat owner in the same sitting/dev:developon a feature whose wave has two work units that both run tests.concurrently.
/dev:develop.Expected outcome: One rule, two consumers, no second copy.
Scenario 15: In a slot, the claim uses the slot's own identities
Acceptance criterion: "…the values come from the identity surface… and the new text defers to it
rather than restating how they are resolved."
Lane: integration-covered —
scripts/lint-conventions.sh(the mechanical check this SREQ adds); the run-observation steps are corroboration performed by the human-uat owner in the same sittingre-explain or contradict it.
Expected outcome: The claim divides what the slot already owns; it does not invent a second way to
decide what the slot owns.
Scenario 16: A newcomer does not confuse this with scenario lanes
Acceptance criterion: "…the two concepts carry different names and each document names the other, so
neither is mistaken for it."
Lane: integration-covered —
scripts/lint-conventions.sh(the mechanical check this SREQ adds); the run-observation steps are corroboration performed by the human-uat owner in the same sittingExpected outcome: A reader meeting both on the same day can tell them apart without being told.
Scenario 17: Edge cases
Lane: human-uat — owner: the operator, dogfooding a run against a real project (this repo declares
e2e,a11yandsecurity-browsernot applicable, so no automated browser lane exists here)Verify: everything serializes and the run still completes; nothing deadlocks waiting for a claim.
Verify: the rules add no ceremony that blocks or slows it beyond carrying its claim.
Verify: the common resource is treated as a collision; the distinct ones do not force extra
serialization beyond it.
Verify: the second dispatch is allowed, and the record shows the first claim ended at the report.
Notes
declaration says validation is the shell test harness plus dogfooding, and these scenarios are written
for that.
chosen project, that is a prerequisite gap to solve before QA, not a reason to weaken the scenario.
design decisions and are settled in
/dev:technical-plan.Run note — sharpening the panel hand-back finding on PO-43-1
phase-outcome:v1is immutable, so this refines that finding rather than editing it.What PO-43-1's finding said: the five requirements-panel subagents' hand-back messages never reached
the driver's inbox; the reports were recovered from their transcripts.
What the full run showed, and it is a sharper claim. Across two panels — 5 requirements lenses and
4 technical-plan reviewers, 9 agents — zero report messages were delivered to the driver, while
every agent's idle notification arrived normally, and the requirements panel's reports were eventually
delivered late, well after PO-43-1 had been posted. One agent re-sent its report on request and that resend
was not delivered either, though its transcript recorded it as sent.
So the channel is not down. Idle signals arrive; final report messages are dropped or arbitrarily
delayed. That is exactly the failure
wait-discipline.md§1a already names — "the harness can deliver'this agent went idle' while its final report is dropped" — so this run corroborates an existing suite
rule rather than discovering a new defect. It is the same class as open issues #44 (an idle signal is not
completion) and #58 (no dead-man coverage; verify DONE as a claim).
What actually recovered the work, both times: reading the subagent transcripts under
~/.claude/projects/<project>/<session>/subagents/*.jsonland extracting the final assistant text. Thetree — here, the transcript — was ground truth; the signal was not. Had the driver read idle as done, a
Tier-3 requirements panel and a 4-reviewer design panel would both have recorded as silent, and the SREQ
would have shipped without the five blocking concerns the design panel found.
Why it belongs on this issue. It is run evidence produced while working #43, and #43's own subject is
the same family of mistake: treating an unreliable signal as evidence. It is not a change to #43's scope —
the meta lane owns whether
wait-discipline.mdneeds strengthening, and #44/#58 are where that lands.Linked: this issue is sibling #65 (recorded by the devwork pipeline).
Linked: this issue is sibling #66 (recorded by the devwork pipeline).
Linked: this issue is sibling #39 (recorded by the devwork pipeline).
Decomposition
Four work units in two waves, following the SREQ's Implementation Scope with one merge.
The merge: the SREQ split the canonical document (its WU-1) from the
journal-template.mdentry types (its WU-2). Those are folded into WU-43-3-1. The SREQ itself says the entry shapes
"are specified here", so the two halves are one design; the
DISPATCH/RELEASEYAML injournal-template.mdand the lifecycle prose inrun-resource-claims.mdmust agree field-for-field,and that agreement is exactly what drifts when two authors write it from the same spec independently.
One author, two files, no shared file with any other unit.
Intra-wave execution is sequential, not parallel. This project runs on a single shared worktree
(
/dev:git-worktreesslot machinery is deliberately unused here — CLAUDE.md §Parallel sessions), soper
/dev:develop§3.1 the wave's units run one complete Test-Writer→Implementer pair at a time. Thewave structure below is an ordering, not a concurrency claim. This is also the cheapest possible
dogfood of the feature under construction: the one contended resource in this repo is the
bash scripts/lint-conventions.shgate itself, and serialize-by-default is what the rule prescribeswhen claims cannot be made disjoint.
Wave 2 is a real barrier. WU-43-3-4's checks assert the shape the first three units produce. A
check authored before its target exists fails for the wrong reason and teaches the author to weaken
it — so the gate lands last, against real content.
Test Writer disposition. WU-43-3-1 through WU-43-3-3 are artifact-shape units: every acceptance
criterion they carry is an assertion about the text of a skill-markdown file, verified by
code-domaininspection and — for the load-bearing ones — by WU-43-3-4's mechanical checks. There is no behaviour
for a behavioural test to exercise, so those three spawn an Implementer only, per
/dev:develop§3.2'sartifact-shape exception, and each records the skip in its outcome summary. WU-43-3-4 spawns both:
its acceptance criteria are behavioural (a check must fire on a violation and stay silent on a
conforming tree), and this repo has a real harness for exactly that in
scripts/test-lint-conventions.sh.Decomposition
Three work units, run strictly in sequence. This supersedes the plan posted as comment 1369, which
had the gate last; the reasoning for the inversion is below and is the substantive part of this plan.
The gate is authored first, and blind
The SREQ's Implementation Scope put
scripts/lint-conventions.shlast, on the argument that "a checkwritten before its target exists fails for the wrong reason." On inspection that argument does not hold,
and the ordering it produces gives up the one piece of test discipline this feature can actually have.
A check asserting "the normative sentence appears in exactly one file" against a tree where it appears
in zero files has failed for precisely the right reason. That is red. Nothing about it is spurious.
What the SREQ's ordering does cost is real. The SREQ names AC-14 and AC-15 as load-bearing and says why:
"the failure mode is one consumer quietly restating the rule instead of referencing it, which reads
correct in isolation and drifts silently." A gate authored after reading the prose it judges is
written to fit that prose — that is the same defect one level up, and it is invisible for the same
reason. Authoring the checks from the acceptance-criteria text alone, before any of the documents exist,
is what makes them an independent assertion rather than a description.
This also restores the 2-phase TDD split, which a documentation feature otherwise forfeits entirely.
There is no behaviour here for a conventional Test Writer to exercise, so the usual answer is to invoke
/dev:develop§3.2's artifact-shape exception and skip the phase. The inversion gives the split backits meaning: WU-43-3-1 is the test-writer half of this feature, at feature scale rather than unit
scale, and it is context-isolated from the implementation by construction — it is authored before the
implementation exists, not merely by a different agent.
Credit where due: this inversion was worked out by the session that paused this run at 3.1 and recorded
it in
PAUSED.md. It is adopted here on its merits.Why three units rather than that note's two
PAUSED.mdproposed the gate plus a single unit carrying all eight documentation files. The argument forinverting is sound; the sizing is not. Eight files — a new canonical document of a few hundred lines, plus
edits into
qa-playbook.md(514 lines) anddevelop/SKILL.md(695 lines) — is a large amount of readingand authoring for one context window, and quality degrades at the end of a full one. The documentation
is therefore split at its natural seam:
design: the SREQ specifies the
DISPATCH/RELEASEYAML and the lifecycle prose that describes it, andthey must agree field-for-field. Two authors working from the same spec independently is exactly how
that agreement drifts.
spawn-prompt rule, a triage rule, a withdrawal rule, a cross-reference, a disambiguating line — and
they share a single concern: cite the canonical document, restate nothing from it. One author holding
that concern across all six is more likely to keep AC-14 honest than three authors holding it once each.
No unit shares a file with any other, so the boundaries stay clean whether or not execution is parallel.
Execution is sequential, and the red baseline is briefed explicitly
This project runs on a single shared worktree — the slot machinery is deliberately unused here
(CLAUDE.md §Parallel sessions) — so per
/dev:develop§3.1 the units run one at a time regardless ofwave structure. The waves above are an ordering.
That has one consequence worth stating rather than discovering: the gate is red from the end of
WU-43-3-1 until the end of WU-43-3-3, which is the point of writing it first. The Implementer
verification bar ("linter clean, full suite green") therefore cannot apply unchanged to the middle unit.
Each brief carries its known-red baseline explicitly:
bash scripts/lint-conventions.shfails only on the new checks, andbash scripts/test-lint-conventions.shproves each new check fires on a violation and stays silent on a conforming tree. Every pre-existing check still passes.run-resource-claims.mdandjournal-template.mdpass. The citation checks are still red — that is the recorded baseline, not a regression.bash scripts/lint-conventions.shis fully green. No exceptions, no documented-red remainder.This run is subject to the rule it is building
The feature's subject is that concurrent actors sharing a resource must declare and honour ownership, and
serialize when they cannot. The one contended resource in this repo is the
lint-conventions.shgateitself, and three sequential units with a single writer each is what the rule prescribes. The SREQ
anticipated this ("this feature's own development is subject to the rule it describes"); the sequencing
above is that dogfood, not a coincidence.
Test Writer disposition
No conventional Test Writer is spawned in this run, and the reason differs per unit:
the harness cases proving the gate fires. A Test Writer for it would be a test of a test.
/dev:develop§3.2: every criterionthey carry is an assertion about the text of a skill-markdown file, verified by
code-domaininspection and by WU-43-3-1's checks. There is no behaviour to exercise.
Each unit records this in its outcome summary, per the exception's logging requirement.
Development journal — WU-43-3-1 (mechanical gate for the run-resource-claims contract)
Files owned:
scripts/lint-conventions.sh,scripts/test-lint-conventions.sh.Authored blind: the documents this gate judges did not exist while it was written.
[2026-08-25] DISCOVERY: the anchors every other work unit must honour
where:
scripts/lint-conventions.sh, section "Run-resource-claims contract (#43)"claude_md_gap: none — this is the contract direction the brief asked for
(the gate names the shape; the prose conforms to it).
This is the mandatory entry. WU-43-3-2 and WU-43-3-3 must honour every item below
verbatim, or the gate stays red against correct-looking prose.
1. The canonical document
Exact path, nothing else will satisfy the gate:
2. The six normative anchors
Each of these six literal fragments must appear in
run-resource-claims.md, andmust not appear in any other
*.mdunderplugin/skills/or.claude/skills/.serialize by defaultan absent key means unknownnon-empty set intersectionsum of concurrently-claimed workersa project that declares nothingno new mandatory blockMatching rules — these are the things that actually bite:
grep -iF), so sentence-initial capitals are fine:"An absent key means unknown…" satisfies anchor 2.
fragment is fixed.
absent. This is the single most likely way to fail this gate by accident — an editor
reflowing a paragraph at 100 columns will silently break anchor 4, which is 36
characters long and easy to land on a wrap point.
fenced example that restates the rule is a restatement just the same.
Zero pre-existing occurrences of any anchor exist in the tree today (verified before
choosing them), so the uniqueness half is currently green and only a new restatement
can turn it red.
3. The two AC-14 consumers must cite by path
These two files must each contain this exact string:
plugin/skills/_shared/procedures/qa-playbook.mdplugin/skills/develop/SKILL.mdThe match is a plain substring, so the quoted in-fence spelling
(
"${CLAUDE_PLUGIN_ROOT}/…/run-resource-claims.md") and the bare prose spelling inbackticks both satisfy it. The
${CLAUDE_PLUGIN_ROOT}/skills/prefix is not optional— tier 3's class-2 rule already requires it for every
_shared/reference in shippedmarkdown, so this anchor is forced by an existing rule rather than invented here.
Only these two are required to cite.
validate-workflow.md,fix-workflow.mdandworktree-discipline.mdare free to cite as well (and should), but the gate does notrequire it — AC-14 names only the QA playbook and
/dev:develop. What the gate doesenforce on them is anchor uniqueness: they may reference the rule, never restate it.
4. AC-15 —
run-resource-claims.mddefers, never derivesMust contain both:
${CLAUDE_PLUGIN_ROOT}/skills/_shared/procedures/slot-isolation.md§7.2(the section sign, not "section 7.2")Must NOT contain any of (this is the derivation ban, matched as an ERE against the
whole file):
derived_suffixesSLOT_PORT_BASE,SLOT_PORT_BLOCK,SLOT_PORT_ENDbase_port/base port/Base Port/BASE_PORTPORT_OFFSET,SLOT_OFFSET,port offset/Port offsetslot… * N/slot… + N, orN * slot/N + slotExplicitly still legal, because naming a value source is not restating a derivation:
SLOT_DB,SLOT_DB_<SUFFIX>,.slot.env, and a port-range literal such as"4080-4089".The SREQ's own vocabulary table uses all four; the conforming fixture in the harness
carries all four precisely so a future edit cannot tighten the ban onto them without a
scenario going red.
5. AC-16 — the cross-reference is mutual
run-resource-claims.mdmust contain${CLAUDE_PLUGIN_ROOT}/skills/_shared/schemas/test-plan.v1.mdplugin/skills/_shared/schemas/test-plan.v1.mdmust contain${CLAUDE_PLUGIN_ROOT}/skills/_shared/procedures/run-resource-claims.md6. AC-4 / AC-7 / AC-9 — the record shapes in
journal-template.mdAdded after the lead's follow-up brief (see the second STRUGGLE entry). Target file:
plugin/skills/_shared/procedures/journal-template.md.Matched CASE-SENSITIVELY, unlike the six prose anchors above — these are YAML keys
and values, which are literal. It matters: the file already says "the YAML dispatch
(
type: complete…)" in prose at line 98, so a case-insensitiveDISPATCHwould pass ontext that defines nothing.
Gate tokens — the entry types (AC-9), which also gate everything below:
type: dispatchtype: releaseIf either is absent the gate reports one finding and suppresses the rest — same
suppression pattern as the canonical document.
Discriminating keys — each must appear once both entry types are defined:
claims:concurrent_with:remedy:distinct-resourcesnone / serialized / distinct-resourcesenumeration was written, without gating a whitespace-fragilea | b | cstringended_by:overran_claim:death-checktype:,timestamp:andactor:are deliberately not gated: the file's existingentry types already carry them, so requiring them would pass no matter what.
waited_ms:is not gated either — the SREQ marks it optional.The driver journal's named home (AC-9) — must appear verbatim,
$FEATURE_FOLDERprefix included:
AC-7's negative half: no line containing
ended_by:may also containtimeout,elapsed, orsilence. A silent actor is released only on a positive death check;one word in an enumeration would undo that.
Uniqueness is deliberately NOT asserted for any of these tokens, unlike the six
prose anchors.
run-resource-claims.mdlegitimately namesoverran_claim(an actorthat used a resource beyond its claim voids its own result) and
claims:(the block adispatched brief carries), so a "stated once" rule would fire on correct prose. What is
asserted is that the shape is defined in
journal-template.md— that is the only filethe keys are required to appear in.
7. AC-5 / AC-6 — what must NOT be added
These two files must contain none of
run-resource-claims,run_resource_claims,resource claim(s)/resource_claim(s)/resource-claim(s), or aclaims:key:plugin/skills/_shared/procedures/readiness-check.mdplugin/skills/setup/SKILL.mdi.e. do not mention this feature on a readiness or setup surface at all. A project
that declares nothing still gets the four suite-owned classes, so its silence must never
surface as a gap or a prompt. (The English word "claiming" is fine — the ban on
claims:requires the colon.
setup/SKILL.mdline 273 already says "migrate work by claiming",and the harness pins that it stays legal.)
And nowhere in
plugin/skills/**/*.mdmay arun_resource_claims:orresource_claims:declaration block appear.
[2026-08-25] DISCOVERY: what the SREQ left to me, and what I chose
where: the AC table's "Mechanical" rows
claude_md_gap: none
The SREQ says AC-14's check asserts "the normative sentences appear in exactly one file"
but never says which sentences. That is the whole judgement call in this unit, and there
was no way to look it up without reading the prose I am supposed to be judging
independently. What I chose, and why:
(the default, the meaning of an absent key, the two overlap tests, the
no-declaration default, the no-new-block guarantee). None states what an actor
does. That distinction is load-bearing: a consumer legitimately has to write
operational sentences — "a void result is re-run under sole ownership before
classification, see " — and a gate that banned those everywhere would force
WU-43-3-3 to write unreadable prose or to weaken the gate. Banning the definitions
costs a consumer nothing, because a consumer has no reason to define anything.
gets exactly one anchor. Fewer would leave a rule restateable without tripping
anything; more would turn conformance into a copying exercise.
claims:itself is deliberately NOT an anchor. Both consumers must emit aclaims block in a dispatched brief, so the token legitimately appears in their text.
Also underspecified: AC-6's "no new mandatory block" has no mechanical definition
anywhere. I read it as two assertions — nothing about this feature appears on a
readiness/setup surface (where a requirement would have to land to take effect), and no
run_resource_claims:/resource_claims:block appears anywhere in the shipped tree.Not covered by this gate, deliberately— SUPERSEDED. AC-4 and AC-9 wereoriginally out of scope: the brief assigned this unit AC-5/6/14/15/16 only, and
inventing anchors for WU-43-3-2's file without a brief to carry them would have been an
unhonoured contract. The lead's follow-up brief closed that gap (it was a brief omission,
not a scope decision) and asked for them, plus AC-7, while the blind property still held.
They are now gated — see §6 of the anchors entry. AC-7 was offered as optional and I
took it:
ended_byadmitting no timeout is one line to check and is the load-bearinghalf of "elapsed silence never releases a claim".
[2026-08-25] DISCOVERY: the fence tracker is the wrong tool for these rules
where:
scripts/lint-conventions.sh, the contract section's header commentclaude_md_gap: none — CLAUDE.md's rule is about scans whose meaning changes by
context, and it says so ("Rules differ by context")
The brief said to reuse the fence tracker rather than line-regex skill markdown. I did
not, and the reason is written into the code so a reviewer meets it before the checks.
The tier-1
$scratchextraction yields shell-fence bodies only. Every rule in thissection is prose-level — does this document cite that one; does this sentence live in
exactly one file — and running them over
$scratchwould restrict them to lookinginside shell fences, which is the exact opposite of what they need.
The concern the CLAUDE.md rule protects against does not arise here, and that is not an
accident of the checks — it is a property they were designed to have:
${CLAUDE_PLUGIN_ROOT}quoting (double-quoted in a fence, bare in prose). Every citation check here matches
the path as a substring, which both spellings contain, so there is nothing to
disambiguate.
example that restates the rule is a restatement.
If a future check in this section ever does depend on quoting or on command position,
it must switch to
$scratch— the tracker is right there and the tier-1 checks show how.[2026-08-25] STRUGGLE: the harness asserted the very thing this unit had to break
attempts: 2
scenario_control_clean_tree(scenario 2, from #57's QA) runs the reallint-conventions.shagainst the real repo and requires exit 0. This unit's entirepurpose is to make that exit 1. Two options:
retires the strictest assertion in the suite to buy a few days of in-flight red.
Took 2. The scenario now accepts a non-zero exit only while
plugin/skills/_shared/procedures/run-resource-claims.mddoes not exist, and eventhen only if every violation line mentions
run-resource-claims— any other check goingred still fails the suite, which is what this scenario was actually protecting. The
moment WU-43-3-3 lands the document, the strict
rc == 0branch takes over again with noedit. Nobody has to remember to tighten it back. That property is why option 2 was
worth the extra branch.
This is also the trick that let the whole unit satisfy its verification bar: lint is red
by design, and the harness is green, at the same time, without either one lying.
[2026-08-25] DISCOVERY:
scripts/test-plugin-gates.shgoes red too, and it is not minewhere:
scripts/test-plugin-gates.sh— outside this work unit's file boundaryclaude_md_gap: none
The number is 6 FAIL / 11 PASS out of 17. I first reported 3, and the lead caught it.
Both measurements were real; the difference is when they were taken, and that turns out
to be the useful part of this entry.
Baseline, with the original
lint-conventions.shrestored: 17 PASS / 0 FAIL.With this unit's version committed: 11 passed, 6 failed, 0 skipped, 17 total.
lint_nonvacuity_on_real_treescenario_24_scan_negative_controlsscenario_34_class1_outside_markdownpositive_control_release_dry_runrelease: gate 1/4 — scripts/lint-conventions.shand stopsscenario_28_release_bump_pathscenario_29_release_first_run_not_resumeThe release chain matters more than the count.
scripts/release.shrunslint-conventions.shas gate 1/4 (release.sh:182-185) before it will tag anything, soa red lint blocks releasing outright.
/dev:promoteis blocked until the contract lands,not merely three harness scenarios.
Why I measured 3, and it is not flakiness. Those three release scenarios operate on a
clone, so they see HEAD, not the working tree. My first run was taken before I
committed: the clone carried the old, clean lint script and all three passed. The lead's
run and my re-measurement were taken after the commit, so the clone carries the red
lint and all three reach gate 1/4. There is a second contributing refusal in the same
direction —
release.sh's Refusal 1: dirty tree (release.sh:96-100) fires before thegates, so a dirty tree short-circuits ahead of lint either way. Neither is order- nor
state-dependent: run this suite against a committed tree or the numbers lie. That is
the lesson worth keeping.
This is the same in-flight red as scenario 2, in a file this unit may not touch. It should
clear itself when the documents land — those scenarios' clones come from the real tree, so
a conforming tree makes conforming clones. It needs a re-run after WU-43-3-3, not a
fix, and the baseline to compare against is 17 PASS / 0 FAIL. If anything is still
red then, the cause is real.
Left as-is deliberately rather than reaching outside the assigned file boundary.
[2026-08-25] COMPLETE: WU-43-3-1
variant: development
iterations: 2 (the first commit covered AC-5/6/14/15/16; the second added AC-4/7/9 on the
lead's follow-up brief. Each was green on its first full run; the eight mutation runs were
verification, not repair — the one genuine repair was a fixture bug, see below)
What landed
scripts/lint-conventions.shgains one section, "Run-resource-claims contract (#43)",placed after the route-through scan and before the tally, plus a paragraph in the file
header. Sixteen checks and one non-vacuity guard, all reporting messages that contain the
literal
run-resource-claimsso the in-flight allowance can filter on them.scripts/test-lint-conventions.shgains seventeen scenarios (5-21), a conforming-fixturebuilder covering all six target files, and the conditional rewrite of scenario 2.
The one real repair:
scenario_rrc_ac4_remedy_key_absentfirst broke its fixture withgrep -v 'remedy:', which deleted the whole line and tookdistinct-resourceswith it —two keys missing for one intended edit, and the count assertion caught it. Changed to
sed 's/remedy:/reason:/', which is also the likelier real-world drift: the enumerationsurvives, the key does not. The count assertion is what found this —
rrc_expectalone would have passed on a fixture that was testing something other than what it said.
Current red set — 5 violations, all expected
No pre-existing check newly fails (baseline was
lint-conventions: clean). The AC-5/AC-6guards and the anchor-uniqueness rule are green today, correctly — they assert an absence
that currently holds. The per-anchor and per-content rules are suppressed while the
document is missing, so one absent file is one finding rather than ten.
Hardest part
Choosing the anchors without reading the prose. Everything else in this unit is
mechanical; that one decision is the unit. The temptation is to pick phrases that are
easy to grep, which produces a gate that is easy to satisfy and catches nothing. The
discipline that helped was asking, for each candidate: would a consumer that
legitimately references the rule ever need to write this sentence? If yes, it is
operational and cannot be an anchor. If no, it is definitional and belongs to the
canonical document alone.
If I did this again
I would mutation-test earlier. Twenty-one scenarios passing on the first run is not
evidence that they discriminate — it is equally consistent with assertions that can
never fail. Eight targeted mutations settled it, each caught by exactly the scenario that
should have caught it and by no other:
rrc_anchor_absent_from_canonical_docrrc_ac15_derivation_restatedrrc_conforming_tree_is_silentrrc_ac5_readiness_gains_required_itemrrc_ac4_remedy_key_absent,rrc_ac7_death_check_value_absentended_bytimeout banrrc_ac7_timeout_based_releaserrc_ac9_driver_journal_unnamedrrc_ac9_entry_types_undefinedThat took about ten minutes total and is the only reason I would sign off on a suite whose
first run was all-green. Run it before believing the greens, not after.
The second thing I would do differently: commit before measuring anything that clones
the repo. My
test-plugin-gates.shcount was wrong by half because three of itsscenarios clone HEAD and I measured with my changes still uncommitted. The run was honest
and the number was still wrong — "which tree did this actually read?" is a question worth
asking of every harness before quoting its output.
Verification, independently re-run by the lead
Not taken from the teammate's report — every number below was re-measured on the committed tree:
bash scripts/lint-conventions.shrun-resource-claims.mdmissing; two consumers not yet citing;test-plan.v1.mdnot yet cross-referencing;journal-template.mdnot yet defining the entry types). No pre-existing check newly fails.bash scripts/test-lint-conventions.shbash -n, exec bits, file boundary-rwxrwxr-x; commits touch these two files onlyEight mutation tests across the two commits: the teammate neutered one part of the gate at a time and confirmed each was caught by exactly the scenario that should catch it, and no other. Sixteen — or twenty-one — green tests on a first run is not evidence a gate discriminates; this is.
One repair inside that process is worth recording as a general lesson: a fixture built with
grep -v 'remedy:'deleted the whole line and took a second required key with it, so the scenario was testing something other than what it claimed. The count assertion caught it (expected 1, got 2); a plain did-it-fire check would have passed. Fixtures now mutate withsed 's/remedy:/reason:/', which is also the likelier real-world drift.Scope correction made during the unit
The unit's original brief scoped it to AC-5/6/14/15/16. The teammate flagged that AC-4, AC-7 and AC-9 are also marked Mechanical in the SREQ and were missing from that list — a gap in the lead's brief, not in the work. It was sent back to close them while
journal-template.md's new entries still did not exist, which is the only window in which the blind-authoring property holds. It declined to invent those anchors unbriefed, which was the right call: an anchor no brief carries is an anchor no author honours, and the check would then fail against correct prose.AC-7 was taken mechanically rather than left to
code-domain inspection, on the reasoning that "death-checkpresent, and noended_byline admittingtimeout/elapsed/silence" is two greps and guards the load-bearing half of elapsed silence never releases a claim — a single word added to that enumeration would undo the rule silently.Carry-forward for cross-unit verification (3.7)
scripts/test-plugin-gates.shis 11 PASS / 6 FAIL on the committed tree, against a baseline of 17 PASS / 0 FAIL. All six trace to the in-flight red lint; three of them are the release path, becauserelease.shrunslint-conventions.shas gate 1/4 — so/dev:promoteis blocked until the contract's documents land, which is a wider blast radius than a harness-only failure.The teammate first reported this as 3 failures; the lead's re-measurement found 6. The mechanism is now understood and is itself a reusable rule: the three release scenarios operate on a clone, so they read
HEAD, not the working tree — the first measurement was taken pre-commit, when the clone still carried the clean lint script. Measure this harness against a committed tree or the numbers lie. Not order- or state-dependence.This needs a re-run after WU-43-3-3, not a fix. If it is still red once the lint is green, the cause is real.
Dev Journal — WU-43-3-2 (run-resource-claims contract + record shapes)
[2026-08-25T10:05] DISCOVERY: the AC-15 derivation ban forbids the SREQ's own risk wording
Context: AC-15's
RRC_DERIV_REbans the literal tokenderived_suffixesinsiderun-resource-claims.md. The SREQ's Technical Risks table names exactly that field as the lever aproject pulls to widen its database pool, and the brief asks me to state that risk in the document.
Pattern: name the lever by description — "the recipe's per-database suffix list (slot-isolation
§1)" — rather than by field name. The reference still lands the reader on the right field; the document
does not restate the derivation.
Why it matters: the two instructions only look like a conflict. The ban is on restating a
derivation, not on pointing at where one lives. Same reasoning applies to the port half: "the port block
the slot owns" is a value source; the arithmetic that produces it is slot-isolation's.
[2026-08-25T10:40] DISCOVERY: the harness's in-flight allowance retires one work unit too early
Context:
scenario_control_clean_treetolerates the #43 contract's own violations only while thecontract is "in flight", and retires that allowance via
rrc_contract_landed(), which is keyed onexactly two artefacts —
run-resource-claims.mdexisting, andtype: dispatchinjournal-template.md. Both are WU-43-3-2's. The moment my unit lands, the allowance lifts, but thegate is still legitimately red on WU-43-3-3's three consumer citations, so the control test fails.
Pattern: the predicate needs the consumer half too — the two citations plus the
test-plan.v1.mdcross-reference. Keyed on all five artefacts it would stay green across every landing order.
Why it matters: verified by simulation rather than argued: a scratch copy of the tree with the
three WU-3 citations appended gives
lint-conventions: clean(rc 0) and21 PASS / 0 FAIL. So theFAIL is an ordering artefact of the predicate, not a defect in this unit's text — and it self-resolves
the moment WU-43-3-3 commits.
scripts/is outside my boundary, so this is reported, not fixed.[2026-08-25T10:55] COMPLETE
Hardest part: honouring AC-15's derivation ban while still stating the SREQ's single-database
ceiling risk, whose lever the SREQ names by the one field token the gate forbids — resolved by naming
the lever descriptively and pointing at slot-isolation §1 for the field itself.
If I did this again: I would grep each anchor for a line break as I wrote it rather than at the
end. One of the six ("a project that declares nothing") landed wrapped across a 100-column break on
the first draft and would have read as absent, exactly as the brief predicted for anchor 4.
Verification, independently re-run by the lead
bash scripts/lint-conventions.shslot-isolation.mdcited by path,§7.2named, zero derivation-token hits,test-plan.v1.mdcross-referenced, all seven journal discriminating keys present, driver journal named verbatim, no timeout-admittingended_by.qa-playbook.mdanddevelop/SKILL.mdnot yet citing by path (AC-14),test-plan.v1.mdnot yet cross-referencing (AC-16).a0ce85etouches exactly the two owned files.scripts/untouched, exec bits intact.The gate was authored before this document existed and was not modified to fit it. The unit reported judging none of the six anchors wrong and made no edit to
scripts/lint-conventions.sh— confirmed by the diff. That is the property the wu-plan's inversion (comment 1372) was for: the checks are an independent assertion, not a description of the prose.Carry-forward — a defect this feature introduced in its own harness
bash scripts/test-lint-conventions.sh→ rc 1, 20 PASS / 1 FAIL (control_clean_tree_no_planted_file).Cause, diagnosed and verified by the unit rather than argued: WU-43-3-1's in-flight allowance retires via
rrc_contract_landed()(scripts/test-lint-conventions.sh:144), which is keyed on only two of the artefacts the gate judges —run-resource-claims.mdexisting, andtype: dispatchpresent injournal-template.md. Both landed with this unit, so the allowance lifted while the gate is still legitimately red on WU-43-3-3's three citations. The predicate is missing the consumer half.The unit verified the resolution by copying the tree to scratch, appending the three WU-43-3-3 citations, and running both gates against the copy:
lint-conventionsclean (rc 0) and the harness 21/21. So this self-resolves the instant WU-43-3-3 commits; no edit toscripts/is required for the feature to go green. Re-verified at 3.7.Left unfixed deliberately. The delivered artefact is correct once WU-43-3-3 lands, and the allowance branch exists only for the transitional window that then closes. Pulling
scripts/test-lint-conventions.shinto WU-43-3-3 would dilute the unit that carries the AC-14 restate-vs-reference discipline — the one place in this feature where that error is most likely. Recorded as a finding on the terminal Phase Outcome instead. The generalisable form: an in-flight allowance keyed on a subset of the artefacts its gate judges will go red on a correct intermediate state.Underspecifications resolved by the unit
derived_suffixesas the lever for the single-database ceiling, but the gate forbids that literal token in this file. Resolved descriptively — "the project's own per-database suffix list in itsparallel_dev:recipe (slot-isolation §1)" — which points the reader at the field without restating the derivation. Same treatment for the port half.jq-readable, but{database: [...]}has unquoted keys. The bridge is pinned explicitly in the triage recipe: onesedquoting the bare keys, after whichjqreads it directly. Both that transform and the intersection filter (including"4080-4089"range expansion) were executed, not just written.map(tostring)) so the intersection is one type throughout.workerspool source. The project's runner default concurrency; the budget test is a sum over everything dispatched and not yet released — not pairwise, which the SREQ's phrasing could be misread as.Authoring note for the next unit
The lead's warning that a long anchor can wrap across a 100-column break and then read as absent was right in principle but aimed at the wrong anchor:
a project that declares nothingwrapped in the first draft, while the 36-character anchor 4 had been placed at line start deliberately. Check every anchor for a line break, not only the longest one.Dev journal — WU-43-3-3 (route the QA and develop consumers through the claims contract)
Feature: qa-intra-run-lane-ownership (issue #43). Final work unit.
Owned files:
qa-playbook.md,validate-workflow.md,fix-workflow.md,develop/SKILL.md,worktree-discipline.md,test-plan.v1.md.2026-08-25T10:05 DISCOVERY: the gate's anchor list is a floor, not the discipline
Context: reading the six normative anchors before writing a word of consumer prose.
Pattern: the anchors are matched as case-insensitive substrings tree-wide, so they catch a
copy-paste but not a paraphrase. Writing each consumer paragraph as an obligation owed to the
canonical document ("what this stage owes it", "read them there") rather than as a summary of it
takes the paraphrase pressure off entirely — there is nothing to summarise if every sentence is a
duty rather than a definition.
Why it matters: the drift AC-14 exists to prevent is a paraphrase, not a copy. The linter cannot
see one; the sentence shape can prevent one.
2026-08-25T10:40 DISCOVERY: the contract has no actor-side disclosure duty
Context: writing the fixer's re-run rule, which is read by a dispatched actor, not the driver.
Pattern: every section of the canonical document addresses the driver — it resolves, compares,
dispatches, records, releases. The one place the actor is load-bearing is the
overran_claimdetection: "the declared-and-honoured model has no enforcement, so the report is the detection."
But nothing in the tree tells an actor to disclose use beyond its claim. A grep for
overran_claimfinds it only in the contract and injournal-template.md's entry shape — never ina consumer an actor actually reads.
Why it matters: the only detection mechanism in a model with no enforcement depends on a
disclosure the contract never asks for. Reported to the lead; the fix belongs in
run-resource-claims.md§5, which is not this unit's file.2026-08-25T10:45 COMPLETE
Hardest part: writing six paragraphs about a rule without ever stating the rule — the temptation
to define "void" inline was strongest in
validate-workflow.md, where the reader is mid-triage and apointer feels like an interruption. Resolved by making each bullet an obligation ("ask the run
record, not the result") whose definition lives elsewhere, so the paragraph is complete as guidance
and empty as doctrine.
If I did this again: read the gate's anchor list before drafting rather than after — it fixes the
vocabulary you must avoid, and the sentences come out shaped correctly the first time.
Verification, independently re-run by the lead on the committed tree
bash scripts/lint-conventions.shlint-conventions: clean— fully green, no documented-red remainderbash scripts/test-lint-conventions.shbash scripts/test-plugin-gates.shde498e6touches exactly the six owned files;scripts/unmodified, exec bits intact; tree cleanThe harness defect recorded against WU-43-3-2 self-resolved, exactly as that unit predicted by experiment:
control_clean_tree_no_planted_filewent green the moment the citations landed, andtest-plugin-gates.shreturned to 17/0 — which also unblocksrelease.sh, since it runs lint as gate 1/4. No fix toscripts/was needed, and none was made. Carried forward only as the generalisable lesson, not as an open defect.The gate was never edited to fit the prose. Across all three units,
scripts/lint-conventions.shwas written once — blind, before any of the documents existed — and never touched again. That is the property the wu-plan's inversion (comment 1372) existed to buy, and it held end to end.The restate-vs-reference discipline, which is what this unit was for
The unit reported nine distinct citation sites and, at each, what it wrote instead of a restatement. Two are worth preserving:
qa-playbook.mdthe citation was added as a row in the existing "What this file owns, and what it defers to" table — which is immediately followed by that file's own "Do not restate any of those here." The reference lands inside an existing no-restatement contract rather than beside one.validate-workflow.mdthe temptation was real and named: a reader mid-triage experiences a pointer as an interruption, so an inline definition of void reads better locally. Resolved by making every bullet an obligation whose definition lives elsewhere — "complete as guidance and empty as doctrine." That phrasing is the operational form of AC-14 and is worth reusing.Anchor uniqueness confirms it mechanically: none of the six normative anchors appears outside
run-resource-claims.md.Gap found in a file this unit could not edit — carried to the Phase Outcome
The contract's only detection mechanism has no producer.
run-resource-claims.md§5 states that an actor whose report shows use beyond its claim (overran_claim: true) voids its own result, and that — because the model is declared-and-honoured with no enforcement — the report is the detection. Butgrep -rn overran_claim plugin/skills/finds the token only inrun-resource-claims.mditself and injournal-template.md's entry shape. No document a dispatched actor actually reads asks it to disclose an overrun.The gap is inherited from the SREQ, which specifies the consequence and the RELEASE field but never the actor-side duty — so it is a spec-level hole, not an authoring miss.
The unit deliberately did not patch it in its own consumers, and the reasoning is correct: putting an actor-side duty in
qa-playbook.mdorfix-workflow.mdwhile the canonical document is silent would be a consumer originating a rule the contract does not hold — precisely the drift AC-14 exists to prevent. Raised as a finding on the terminal Phase Outcome instead.Product observations raised for the Phase Outcome
validate-workflow.mdStep 3 now routes unattainable isolation to structural, but the lists that actually define structural causes live in_shared/domains/*and name only missing tooling/credentials, config variants and human-only judgment. A validator matching against its domain file may not recognise the new route — which turns a routed finding back into a bare skipped count, the exact failure that routing exists to prevent.qa-playbook.mdprecondition 5 now carries five distinct obligations (sibling-slot ownership, the slot identity surface, the end-to-end assertion, the run-time port guard, and the intra-run case). Correct and cohesive, but the longest precondition by a wide margin. A split was considered and rejected because other documents cite preconditions by number and splitting renumbers them.Linked: this issue is sibling #67 (recorded by the devwork pipeline).
Linked: this issue is sibling #68 (recorded by the devwork pipeline).
Linked: this issue is sibling #256 (recorded by the devwork pipeline).
Linked: this issue is sibling #257 (recorded by the devwork pipeline).
Linked: this issue is sibling #354 (recorded by the devwork pipeline).
Linked: this issue is sibling #483 (recorded by the devwork pipeline).
Merged to
main— and why, given QA did not exit cleanfeature/43-qa-intra-run-lane-ownershipis onmainat0200516, a clean fast-forward of 23commits, on the operator's explicit decision.
This is not a claim that QA passed. Phase Outcome PO-43-11 stands:
next_state: requirements,because the exit invariant requires a sweep that finds nothing modified after it and sweep 7 found
six. What was decided is narrower: the deliverable other projects consume is verified sound, and
the open question is about this repo's own verification tier, so the two were separated.
Where the open findings actually live:
plugin/)scripts/)Both stopping-rule triggers are in the harness, not in what ships.
CR-87 was deliberately left unfixed and is now signposted in place (commit
3c867d8): a commenton
step4_dispositionsays which of the two to trust and points here. It is the sharpest evidencefor #483's question — a fix changed the document and left the restatement in the same file it was
editing, and nothing noticed. Fixing it would have closed the exhibit while leaving the pattern.
Rebase: 20 commits behind at merge time (the #46 supervise feature). One conflict, in
CLAUDE.md,purely additive — both sides had appended a learning. Both kept.
Gates at the merged commit:
lint-conventions.shclean ·test-lint-conventions.sh29 PASS / 0FAIL ·
test-run-resource-claims.sh65 PASS / 0 FAIL / 0 RED / 0 XPASS ·test-plugin-gates.sh17/17. Zero red markers remain.
Record-keeping note:
/dev:integrateis not among this session's available skills, so the mergewas performed with
gitdirectly and this comment stands in for the record that skill would havewritten. Flagging it rather than leaving a gap.
Follow-ups: #483 (verification tier — the requirements pass this routes to) · #256 (parser set,
four entries now) · #257 (drift design question) · #354 (capability gaps, including the executable
global sum test).