docs(decisions): ADR 0001 — canary 3+3 mandatory; orchestrator cadence is PM-style #41

Merged
pdurlej merged 4 commits from claude/orders/adr-0001-canary-pm-cadence into main 2026-05-03 20:56:31 +02:00
Owner

Summary

Establishes decisions/ directory at repo root + ADR 0001 codifying the synthesis from operator's thesis-antithesis-synthesis observation on 2026-05-03.

Context — what triggered this ADR

Between 2026-05-01 and 2026-05-03, two gaps surfaced:

Gap 1 — canary skipped under productivity pressure. 6 PRs (#32, #33, #34, #35, #36, #40) were merged without firing the 3+3 ensemble review. Up to 9 if Codex's wave 3 PRs (#37/#38/#39) didn't fire either. Smoke.sh (PR #40) on first run caught real image drift on n8n-worker — one concrete example of what slips through without independent voices.

Gap 2 — orchestrator producer-mode. After operator feedback "less meta, more value," orchestrator (Pan Herbata / claude) over-corrected: produced 5 PRs in ~1h, ended every turn with options-list to look productive, no strategic step-back. Operator-as-non-technical-owner became bottleneck without independent eyes.

Operator named the dialectic explicitly:

  • thesis: "less meta, more value"
  • antithesis: orchestrator overproduces, bypasses canary
  • synthesis: this ADR

Decision (5 rules)

  1. Canary 3+3 mandatory per merge for PRs touching modules/, schema/, prompts/, tests/, control-plane/, decisions/. Self-validation insufficient (author signal, not independent verification).
  2. Override is operator's, not orchestrator's. Explicit on PR thread; orchestrator cannot self-grant.
  3. Orchestrator cadence is PM-style. 1 substantial change + canary + product status, NOT 4 changes + options-list.
  4. Strategic stops scheduled. Every N=3 cycles or operator-asked: product overview (not per-cycle status).
  5. Backlog gets retroactive canary. Findings feed fix-forward PRs; revert only at operator decision.

Roles formalized

  • piotr — owner (final decisions, merges, business calls)
  • claude / Pan Herbata — orchestrator-as-PM (articulates work, ensures quality gates fire, holds strategic context)
  • Codex — producer (executes via master prompts)
  • 3+3 reviewers — independent quality voices (claude / codex / glm × tech / product)

Compliance path

  • Today: convention (this ADR + master prompts already include canary instruction)
  • Future ADR 0002: Forgejo Actions enforcement (block merge if state/reviews/PR-N/decision.json missing or != approve_merge)
  • Future small PR: PR template adds "Canary status" checkbox

Self-test

This ADR is the first PR under the new convention. Its own 3+3 canary review is the test that the rule applies even to the ADR defining it.

Verification

  • decisions/ convention established at repo root (consistent with flat repo style)
  • ADR follows Michael Nygard format (Context / Decision / Consequences / Compliance)
  • References existing artifacts (charter §3 / §6, CLAUDE_SELF_INSTRUCTIONS §11/§12/§13, OPEN_LOOPS entries)
  • Names future ADRs (0002 CI enforcement, 0003 Codex-for-Codex pattern) so they have a home
  • Operator's verbatim framing preserved (thesis-antithesis-synthesis, owner-vs-PM-vs-producer roles)
  • Operator-readable: 116 lines, ~2.7k tokens, prose-heavy not bullet-spam

Test plan

  • Operator readback: does the ADR capture what we agreed to in chat?
  • Canary 3+3 fires on this PR (it's the first one under the new rule — must pass its own test)
  • After merge: subsequent PRs (n8n-worker drift fix, smoke-extras, etc.) fire canary before merge
  • After ~3 cycles under new rule: review whether cadence works (operator unblocked, canary findings actionable, producer-mode not creeping back in)
## Summary Establishes `decisions/` directory at repo root + ADR 0001 codifying the synthesis from operator's thesis-antithesis-synthesis observation on 2026-05-03. ## Context — what triggered this ADR Between 2026-05-01 and 2026-05-03, two gaps surfaced: **Gap 1 — canary skipped under productivity pressure.** 6 PRs (#32, #33, #34, #35, #36, #40) were merged without firing the 3+3 ensemble review. Up to 9 if Codex's wave 3 PRs (#37/#38/#39) didn't fire either. Smoke.sh (PR #40) on first run caught real image drift on `n8n-worker` — one concrete example of what slips through without independent voices. **Gap 2 — orchestrator producer-mode.** After operator feedback "less meta, more value," orchestrator (Pan Herbata / claude) over-corrected: produced 5 PRs in ~1h, ended every turn with options-list to look productive, no strategic step-back. Operator-as-non-technical-owner became bottleneck without independent eyes. Operator named the dialectic explicitly: - thesis: "less meta, more value" - antithesis: orchestrator overproduces, bypasses canary - synthesis: this ADR ## Decision (5 rules) 1. **Canary 3+3 mandatory per merge** for PRs touching `modules/`, `schema/`, `prompts/`, `tests/`, `control-plane/`, `decisions/`. Self-validation insufficient (author signal, not independent verification). 2. **Override is operator's, not orchestrator's.** Explicit on PR thread; orchestrator cannot self-grant. 3. **Orchestrator cadence is PM-style.** 1 substantial change + canary + product status, NOT 4 changes + options-list. 4. **Strategic stops scheduled.** Every N=3 cycles or operator-asked: product overview (not per-cycle status). 5. **Backlog gets retroactive canary.** Findings feed fix-forward PRs; revert only at operator decision. ## Roles formalized - **piotr** — owner (final decisions, merges, business calls) - **claude / Pan Herbata** — orchestrator-as-PM (articulates work, ensures quality gates fire, holds strategic context) - **Codex** — producer (executes via master prompts) - **3+3 reviewers** — independent quality voices (claude / codex / glm × tech / product) ## Compliance path - **Today**: convention (this ADR + master prompts already include canary instruction) - **Future ADR 0002**: Forgejo Actions enforcement (block merge if `state/reviews/PR-N/decision.json` missing or != approve_merge) - **Future small PR**: PR template adds "Canary status" checkbox ## Self-test This ADR is the first PR under the new convention. Its own 3+3 canary review is the test that the rule applies even to the ADR defining it. ## Verification - [x] `decisions/` convention established at repo root (consistent with flat repo style) - [x] ADR follows Michael Nygard format (Context / Decision / Consequences / Compliance) - [x] References existing artifacts (charter §3 / §6, CLAUDE_SELF_INSTRUCTIONS §11/§12/§13, OPEN_LOOPS entries) - [x] Names future ADRs (0002 CI enforcement, 0003 Codex-for-Codex pattern) so they have a home - [x] Operator's verbatim framing preserved (thesis-antithesis-synthesis, owner-vs-PM-vs-producer roles) - [x] Operator-readable: 116 lines, ~2.7k tokens, prose-heavy not bullet-spam ## Test plan - [ ] Operator readback: does the ADR capture what we agreed to in chat? - [ ] **Canary 3+3 fires on this PR** (it's the first one under the new rule — must pass its own test) - [ ] After merge: subsequent PRs (n8n-worker drift fix, smoke-extras, etc.) fire canary before merge - [ ] After ~3 cycles under new rule: review whether cadence works (operator unblocked, canary findings actionable, producer-mode not creeping back in)
Establishes decisions/ directory at repo root (matches flat repo style:
PLATFORM_CHARTER, CONSTITUTION, INDEX, OPERATOR_ACTIONS at root).

ADR captures the synthesis of the thesis-antithesis-synthesis cycle that
landed on 2026-05-03. Operator (piotr) named the dialectic explicitly:
- thesis: "less meta, more value" feedback
- antithesis: orchestrator over-corrected into producer-mode (5 PRs in
  ~1h, all without firing 3+3 canary, ending each turn with options-list
  to look productive)
- synthesis (this ADR): work-cycle rules that exit antithesis without
  rebounding to thesis

Concrete gap measured: 6 PRs (#32, #33, #34, #35, #36, #40) merged
without canary; up to 9 if Codex's wave 3 PRs (#37/#38/#39) didn't fire
either. Smoke.sh first run on n8n-worker immediately surfaced real image
drift (sha256:72e2242d5d3b in manifest vs sha256:74660bbc672e live) — one
concrete example of what slips through without independent voices.

Five rules:
1. Canary 3+3 is mandatory per merge for PRs touching modules/, schema/,
   prompts/, tests/, control-plane/, decisions/
2. Override is operator's, not orchestrator's (must be explicit on PR
   thread; orchestrator may not self-grant)
3. Orchestrator cadence is PM-style: 1 substantial change + canary +
   product status, NOT 4 changes + 5-option list
4. Strategic stops scheduled (every N=3 cycles or operator-asked) —
   product overview, not per-cycle status
5. Backlog of unreviewed merged PRs gets retroactive canary; findings
   feed fix-forward PRs (revert only at operator decision)

Roles formalized:
- piotr: owner (final decisions, merges, business calls)
- claude / Pan Herbata: orchestrator-as-PM (articulates work, ensures
  quality gates fire, holds strategic context)
- Codex: producer (executes via master prompts)
- 3+3 reviewers: independent quality voices (claude/codex/glm × tech/
  product) — operator's eyes since operator is non-technical owner

Compliance:
- Today: convention (this ADR + master prompts include canary
  instruction)
- Future ADR 0002: CI enforcement (Forgejo Actions blocks merge if
  state/reviews/PR-N/decision.json missing or != approve_merge)

Self-test: this ADR is itself the first PR under the new convention. Its
own canary 3+3 review is the test that the rule applies even to the
ADR that defines it.

Length: 116 lines, ~2.7k tokens. Operator-readable.
Addresses canary 3+3 findings on the original ADR (PR #41, ELEVATED defer):

1. HIGH risk product-gpt: "Mandatory rule unenforced at operator decision
   surface" — adds Rule 1a (inline enforcement until ADR 0002): every PR
   description must contain link to canary decision_packet.md. Owner-side
   mobile-scan signal:  approve_merge /  defer / ⚠️ skip-with-reason /
    no-canary-don't-merge.

2. MEDIUM tech-claude opportunity: clarify decisions/ ADR edits fire canary
   despite being *.md (doc-only carveout doesn't cover governance).

3. MEDIUM product-claude opportunity: add cycle-counter.md anti-hidden-state
   mechanism for Rule 4 N=3 strategic-stop cadence.

Open loop entry added for ADR 0002 (CI enforcement) with deadline target
end of W21 2026 (~2026-05-24, ~3 weeks). Owner: orchestrator.

Backlog scope-down (operator decision 2026-05-03): retroactive canary on
merged PRs (#32/#33/#34) skipped per ROI argument — findings on already-
merged code are post-mortem with no leverage; smoke.sh continuous drift
detection covers regressions. Canary fires on #40 (still open) and
forward.

Files changed: 3
- decisions/0001-canary-mandatory-pm-cadence.md (+18 lines: Rule 1a, ADR
  edit clarification, cycle-counter mechanism note in Rule 4)
- state/L3/OPEN_LOOPS.md (+1 entry: ADR 0002 deadline)
- state/cycle-counter.md (new file: counter file with current state +
  increment rule + cycle-definition)

Per Rule 1a self-test: this PR will have canary status link in description
once re-fired. The amendment exists because the original ADR didn't have
inline enforcement — meta-test that the rule applies to the rule-defining
ADR.
Canary on amended PR #41 (#41 v2) returned ELEVATED defer with three sharp findings:

1. HIGH (product-gpt): "Manual cycle counter recreates the hidden-state burden it is meant to prevent" — original cycle-counter.md required manual increment per merge = orchestrator-memory in .md wrapper, exact failure mode ADR is bounding.

2. HIGH (product-claude): "PR description empty — violates Rule 1a on the very PR that defines it" — meta-violation, ADR adding Rule 1a didn't itself follow it.

3. MEDIUM (product-gpt): "Canary scope is ambiguous enough to fail on mobile approval" — Rule 1a described 4 emoji states but no fixed string format for CI parsing.

Fixes in this commit:

a) state/cycle-counter.md — REWRITTEN as boundary-marker only. Cycle count is now DERIVED from git history via `git log --merges --count <sha>..origin/main`. File stores only last_strategic_stop_sha + artifact + timestamp. No per-merge increments. Fixes HIGH #1.

b) state/cycle-count.sh — NEW helper. Reads boundary marker, runs git log, reports under_threshold|at_threshold|over_threshold + count. Tested working: returns 0 cycles since boundary 623c804 (correct, that's the SHA before this PR series).

c) decisions/0001 Rule 4 — UPDATED to reflect git-derived counter mechanism, cites canary finding that surfaced the original bug.

d) decisions/0001 Rule 1a — ADDED format spec: "Canary status: <state> — <link-or-reason>" with state ∈ {approve_merge|defer|operator_override|missing}. Stable contract for ADR 0002 CI grep. Fixes MEDIUM #3.

e) .forgejo/pull_request_template.md — NEW. Bootstraps Rule 1a per tech-claude opportunity finding ("close chicken-and-egg in one PR rather than two"). Default Canary status line is "missing" with reminder to fire canary; PR author replaces with actual status before requesting merge. Closes meta-violation HIGH #2 (this PR's own description gets the status line).

Files changed: 4
- decisions/0001-canary-mandatory-pm-cadence.md (Rule 1a format spec, Rule 4 git-derived)
- state/cycle-counter.md (rewritten boundary-marker only)
- state/cycle-count.sh (new helper, bash parameter expansion handles colons-in-timestamps safely)
- .forgejo/pull_request_template.md (new, template-default Canary status line)

Per Rule 1a self-test: this PR's description gets updated post-push with proper "Canary status: defer — link to v2 decision_packet.md" once canary re-fires.
Per operator 4-iter cap on canary + revert/rewrite smoke.sh:
- ADR 0001 v3 residuals (self-referential without CI, Rule 5 scope-down, PR template enforce gap)
- Smoke.sh rewrite with concrete bugs (drift identifier, container prefix, deps, dead code)
Sign in to join this conversation.
No reviewers
No labels
W6d-automerge-calibration
agent/claude-code
agent/codex
agent/hermes
agent/iskra
agent/ollama
agent/patchwarden
automerge-candidate
class/security-sensitive
cutover-gate
dependency/blocked
dependency/blocks-others
dependency/cross-repo
dependency/needs-confirmation
domain:agents
domain:ci
domain:docs
domain:forgejo
domain:infra
domain:memory
domain:runtime
domain:signal
domain:ux
flow/architecture
flow/blocked
flow/deployed
flow/done
flow/implementation
flow/intake
flow/maintained
flow/observed
flow/ready
flow/refining
flow/retired
flow/review
iterating
judge/codex-candidate
judge/hermes-candidate
judge/low-confidence
judge/needs-refinement
judge/operator-needed
judge/p0
judge/p1
judge/p2
judge/p3
judge/park
judge/patchwarden-candidate
judge/stale-priority
kind/adr
kind/bug
kind/chore
kind/feature
kind/infra
kind/ops
kind/refactor
kind/research
large-impact
merge/auto
merge/manual
merge/manual-dependency-conflict
merge/manual-failing-tests
merge/manual-merge-conflict
merge/manual-missing-review
merge/manual-operator-preference
merge/manual-red-zone
merge/manual-security-sensitive
merge/manual-unclear-scope
merge/manual-unknown
meta
mode:operator-only
mode:patchwarden-iskra-approved
mode:safe-auto
needs-operator-decision
needs-triage
not-ready
observed/erroring
observed/needs-followup
observed/pending
observed/retire-candidate
observed/unused
observed/used
operator-emotional
owner-attention
phase/02
phase/03
priority:p0
priority:p1
priority:p2
priority:p3
proposed
ready-for-agent
ready-for-operator
recovery
review:claude-reviewed
review:codex-reviewed
review:dziadek-reviewed
review:needs-human
risk/exposure
risk/process
risk/product
risk/runtime
safety:external-write
safety:no-prod-mutation
safety:prod-impact
safety:secret-touch
size/large
size/medium
size/small
size/tiny
size/unknown
source/adr
source/agent-generated
source/manual
source/operator-chat
source/voice-note
status:blocked
status:codex-ready
status:merged:pending-evidence
status:needs-evidence
status:operator-needed
status:parked
tier/full
tier/lite
tier/stacked
tier:0-platform-substrate
tier:1-iskra-value-layer
tier:2-tools-products-modules
type:bug
type:chore
type:docs
type:feat
type:policy
type:research
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
pdurlej/platform!41
No description provided.