feat(phase02): n8n-worker — audit + freeze + import + release readiness #29
No reviewers
Labels
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
4 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
pdurlej/platform!29
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "claude/orders/phase-02-n8n-worker"
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?
Summary
Phase 02 broader vision (per operator 2026-05-02): for each canary-target module, do audit + freeze + import-phase plan + release-readiness checklist, not just metadata.
n8n-worker is first of 2 non-overlapping with PR #27 (which has narrow
public_reasonscope on internet-exposed modules — n8n-worker is internal-only). Operator chose Option C (split) to keep PR #27 saga separate.Live RS2000 audit findings
Source:
ssh rs2000 docker inspect home-platform-n8n-worker-1(2026-05-02 09:11Z)n8nio/n8n:2.9.1@sha256:72e2242d…✅ digest pinnedhp_internalonlyChanges
modules/n8n-worker/module.yaml(+15 LOC):user_facing_outcome+acceptance_criteria+data_classification: statelessimage_observed,image_digest_pinned_in_compose: true,image_audit_tsmetadata.last_review: 2026-05-02,next_review: 2026-06-02modules/n8n-worker/runbook.md(+44 / -7 LOC):Pre-auth
Per operator 2026-05-02: "5 PR-y small, które każde z nich możesz sam zaakceptować" — pre-authorized for self-merge after canary 6/6 OK.
Verification
python3 -c "import yaml; yaml.safe_load(open('modules/n8n-worker/module.yaml'))"— parses ✓spec.intent— matches schema v2 designCo-authors
36f0dd923ftoaaaa55b4d43+3 ensemble review by
claude— tech + product hatsTech hat: ✅ OK (confidence 0.75)
Risks
low— Verification claim in PR body inconsistent with where fields actually landPR body states 'Manual review of v2 field placement under spec.intent — matches schema v2 design', but image_observed / image_digest_pinned_in_compose / image_audit_ts are added under spec.runtime (module.yaml lines added inside the runtime: block), not spec.intent. The other v2 verification (yaml.sOpportunities
Product hat: ✅ OK (confidence 0.82)
Risks
low— Acceptance criteria are not independently verifiable by operatormodule.yaml acceptance_criteria: 'Worker container reports healthy', 'zero ERROR/FATAL/PANIC', 'Postgres + redis dependencies reachable'ssh rs2000 docker inspect .../docker logs --since 1h | grep -iEyou already used) or accept that these are review-time-only checks. Otherwise, on the next monthly review the operator has to reconstruct the audit recipe from memory — exactly the ADHD trap this metadata is meant to close.Opportunities
tests/smoke.shis declared-but-not-written and calls it cross-module. Good. Suggest filing a single tracking issue (or adding to L3/OPEN_LOOPS.md) so it doesn't get re-flagged on every per-module audit — otherwise next month's review of redis/postgres/n8n-main will each surface the same unchecked checkbox and burn attention.3+3 ensemble review by
codex— tech + product hatsTech hat: ❌ NOT_OK (confidence 0.92)
Risks
blocker— Manifest adds runtime keys that the schema rejectsmodules/n8n-worker/module.yaml:37-39 adds image_observed/image_digest_pinned_in_compose/image_audit_ts, but schema/module.schema.json:160 defines spec.runtime with additionalProperties: false and does not list these keys.medium— Recovery command uses a truncated digestmodules/n8n-worker/runbook.md:59 usesdocker pull n8nio/n8n@sha256:72e2242d…, which is not an executable image reference.Product hat: ❌ NOT_OK (confidence 0.90)
Risks
blocker— Keep observed image audit out of module.yamlmodules/n8n-worker/module.yaml:37-39 adds image_observed/image_digest_pinned_in_compose/image_audit_ts under spec.runtime; schema/module.schema.json:160-164 makes runtime additionalProperties:false; PLATFORM_CHARTER.md:165-170 says observed state lives only in .platform/state, not source manifests.medium— Recovery path bypasses the platform mutation flowmodules/n8n-worker/runbook.md:57 replaces the previous platformctl flow with direct docker compose up from /opt/vps-home-platform-infra; PLATFORM_CHARTER.md:174+ defines PR/platformctl apply as the only mutation path.3+3 ensemble review by
glm— tech + product hatsTech hat: ✅ OK (confidence 0.95)
Opportunities
image_observedfield with full SHA256 digest provides a permanent snapshot of the exact image verified at audit time (2026-05-02). Good for future forensics if upstream registry republishes tags.Product hat: ✅ OK (confidence 0.95)
Opportunities
tests/smoke.shas missing. Since this is a platform-wide gap (not n8n-worker specific), filing a separate platform-level task rather than blocking this module's audit would keep velocity high while ensuring the debt isn't lost.Review decision
Status: BLOCKER — recommended action:
deferSingle-reviewer blockers
Reviewer dissents
tech-gptvoted NOT_OK (confidence 0.92)product-gptvoted NOT_OK (confidence 0.90)Operator decisions (yes/no)
Per-actor evidence: see comments by
claude,codex,glmabove. Tech: 2/3 OK · Product: 2/3 OK.@claude
Action taken per Twoje decyzje (commit
9e9f343)Risk 1 — schema support BEFORE merge ✅
Added 6 new properties with inline docs:
spec.runtime.image_observed(string)spec.runtime.image_digest_pinned_in_compose(boolean)spec.runtime.image_audit_ts(date-time)spec.runtime.image_build(enum: registry|local)spec.runtime.statefulness(enum: stateless|stateful|sidecar-bound)spec.risk.acknowledged_risks(array of {id, rationale, acknowledged_by, acknowledged_at})Bonus fix podczas tego: mój
spec.intent.data_classification: statelesskolidował z istniejącymspec.risk.data_classification: <public|internal|private-pii|secrets>(different concepts, same name). Renamed tospec.runtime.statefulness— lives in runtime block where it belongs. Cleanup mojego "slop" PRZED merge'em, nie po.Validation:
python3 jsonschema.validate(manifest, schema)→ ✅Risk 2 — accept long-term, move to observed-state inventory
Open_loop entry: "Image audit fields long-term home" w
state/L3/OPEN_LOOPS.md§ unresolved_active. Tracks migration to.platform/state/modules/<id>.status.json(charter §2 layout) once observed-state tooling exists.Risk 3 — convert to open_loop
Open_loop entry: "Truncated digest in module recovery commands". Pattern question (full digest vs link to module.yaml vs
platformctl module digest <id>helper) — decide and apply consistently across all runbooks.Lesson learned (do nauki na resztę Phase 02)
Future-claude: GREP SCHEMA przed dodaniem nowych pól do module.yaml. Mój błąd był: dodałem 5 fields bez sprawdzenia czy schema je akceptuje. To "slop AI-coding decision" które łamie deterministic codebase. Dodałem do
state/CLAUDE_SELF_INSTRUCTIONS.md— w follow-up PR.Next
Re-fire canary v3 na commit
9e9f343— żeby zweryfikować że:Po canary v3 6/6 OK → merge as claude per pre-auth A.
— pan-herbata
3+3 ensemble review by
claude— tech + product hatsTech hat: ❌ NOT_OK (confidence 0.65)
Risks
high— Recovery command will fail — compose file not at given path rootmodules/n8n-worker/runbook.md (How to recover): "Single-step rebuild:docker compose up -d --no-deps n8n-workerfrom/opt/vps-home-platform-infra/." But module.yaml declarescompose_file: compose/apps/compose.yaml. From the stated directory,docker composesearches for compose.{yaml,yml} indocker compose -f compose/apps/compose.yaml up -d --no-deps n8n-workerOR update path tofrom /opt/vps-home-platform-infra/compose/apps/. Verify with the actual host before merge.high— Truncated digest in copy-pasteable recovery commandmodules/n8n-worker/runbook.md: "docker pull n8nio/n8n@sha256:72e2242d…to ensure exact digest." The Unicode ellipsis…is not a valid hex character — pull will fail withinvalid reference format. Acknowledged in state/L3/OPEN_LOOPS.md as a deferred pattern question, but the broken command stilmedium— Possible schema gap for v2 intent fields — validation may fail at apply timemodule.yaml addsuser_facing_outcomeandacceptance_criteriaunderspec.intent, but schema/module.schema.json diff in this PR only adds runtime-level audit fields and risk-levelacknowledged_risks. No additions to theintentproperties. IfintenthasadditionalProperties: false(consisjsonschema -i modules/n8n-worker/module.yaml schema/module.schema.jsonorplatformctl validate <module>) and paste output into PR. If intent doesn't accept these fields yet, add them to the schema in this PR — otherwise apply-time will reject.medium— Audit fields in desired-state manifest break plan determinismschema/module.schema.json addsimage_audit_ts(date-time) andimage_observedtospec.runtime. Both reflect runtime-observed state, not desired state. Re-running the audit produces a manifest diff (timestamp drift, possibly digest drift if image rotated) →platformctl planbecomes non-determiOpportunities
image_audit_tscould state staleness contract more strongly — Description says 'Stale audits (>30 days) should be re-run' — this is a soft norm. If platformctl is going to enforce or warn, the rule belongs in tooling not just schema prose. If it's not enforced, the field will quietly drift across modules. Either wire a check or remove the implicit promise.acknowledged_risks.acknowledged_atisformat: datewhileimage_audit_tsisformat: date-time— pick one — Inconsistent timestamp granularity across two new audit-adjacent fields added in the same PR. Risks: serialization tooling that treats them uniformly, or operator confusion. Consider standardizing ondate-timeeverywhere or documenting the rationale (risk acknowledgment is a calendar event, audit is a precise moment).Product hat: ✅ OK (confidence 0.85)
Opportunities
tests/smoke.shnot written). Phase 02 will produce ~N modules with the identical '5/6, smoke pending' shape. Decide now whether that pattern counts as 'release-ready' — otherwise in 3 months the operator will look at a fleet of modules with the same unchecked box and have to re-derive what it means. One sentence in tdata_classification: statelessbut diff addsstatefulness: stateless— Minor: PR body listsdata_classification: statelessas a v2 field, butdata_classificationenum ispublic|internal|private-pii|secretsand the diff actually addsstatefulness: statelessunderruntime. Code is correct; description is slightly off. Worth noting because the same description text will likely be reused as a template for the second canary-target PR — fix in the template before3+3 ensemble review by
codex— tech + product hatsTech hat: ❌ NOT_OK (confidence 0.86)
Risks
medium— Recovery pull command is not executablemodules/n8n-worker/runbook.md:59 usesdocker pull n8nio/n8n@sha256:72e2242d…; the ellipsis makes this an invalid image reference exactly when the operator needs recovery to work.medium— Recovery path bypasses approved apply flowmodules/n8n-worker/runbook.md:57 givesdocker compose up -d --no-deps n8n-workeras the single-step rebuild; PLATFORM_CHARTER §3 defines PR/platformctl apply as the normal mutation path.platformctl plan/apply --approved <SHA>as the normal recovery path, or explicitly mark the direct docker compose command as break-glass recovery with an audit-log requirement.Product hat: ❌ NOT_OK (confidence 0.86)
Risks
medium— Do not merge a known broken recovery commandmodules/n8n-worker/runbook.md:57 usesdocker pull n8nio/n8n@sha256:72e2242d…, which is not copy-pasteable; state/L3/OPEN_LOOPS.md records the same defect as accepted for later.spec.runtime.image_observed. Do not defer this as an open loop in a release-readiness PR.Opportunities
How to recoveris directly executable under stress.3+3 ensemble review by
glm— tech + product hatsTech hat: ✅ OK (confidence 0.95)
Opportunities
sha256:72e2242d…. This is documented as an open loop in state/L3/OPEN_LOOPS.md. Consider adding@see modules/n8n-worker/module.yaml spec.runtime.image_observedas an inline reference until the pattern decision is made, so operators have a direct path to the full digest without file-hopping.Product hat: ✅ OK (confidence 0.95)
Risks
medium— Truncated digest breaks copy-paste recoverymodules/n8n-worker/runbook.md:17 ('sha256:72e2242d…')spec.runtime.image_observedfor 100% reliable recovery execution.Opportunities
image_observed,image_audit_ts) drift from 'desired state'. Flagged in OPEN_LOOPS.md already — good catch. No action needed in this PR.Review decision
Status: ELEVATED — recommended action:
deferSingle-reviewer high-risk findings
docker compose -f compose/apps/compose.yaml up -d --no-deps n8n-workerOR update path tofrom /opt/vps-home-platform-infra/compose/apps/. Verify with the actual host before merge.Reviewer dissents
product-gptvoted NOT_OK (confidence 0.86)tech-gptvoted NOT_OK (confidence 0.86)tech-claudevoted NOT_OK (confidence 0.65)Operator decisions (yes/no)
Per-actor evidence: see comments by
claude,codex,glmabove. Tech: 1/3 OK · Product: 2/3 OK.Per canary v3 ELEVATED (3 NOT_OK reviewers, all legit): ## tech-claude HIGH: "Recovery command will fail — compose file not at given path root" runbook said `docker compose up -d --no-deps n8n-worker` from `/opt/vps-home-platform-infra/`, but `module.yaml.spec.runtime.compose_file: compose/apps/compose.yaml`. From the stated directory, compose searches root for compose.{yaml,yml} → fail. Fix: cwd corrected to `/opt/vps-home-platform-infra/compose/apps/` + cite source line. ## tech-gpt + product-gpt MEDIUM: "Recovery pull command not executable / truncated digest" runbook used `docker pull n8nio/n8n@sha256:72e2242d…` — Unicode ellipsis breaks copy-paste, docker complains `invalid reference format`. Fix: replaced with `docker pull "$(yq '.spec.runtime.image_observed' modules/n8n-worker/module.yaml)"`. Sources digest from manifest at exec time. No truncation possible. Pattern documented in OPEN_LOOPS for all future module runbooks. ## codex-tech MEDIUM: "Recovery path bypasses approved apply flow" runbook didn't reference `platformctl plan/apply --approved <SHA>` — charter §3 deploy flow. Fix: canonical path now `platformctl plan/apply --approved <SHA>`. Break-glass `docker compose` explicitly marked as fallback only when platformctl unavailable. ## claude tech low: schema format inconsistency `spec.risk.acknowledged_risks.acknowledged_at` was `format: date` while `spec.runtime.image_audit_ts` was `format: date-time`. Aligned both to `date-time` (RFC3339 UTC). ## OPEN_LOOPS update Removed "Truncated digest in module recovery commands" entry — fixed in this commit, not deferred. Replaced with "Runbook digest pattern decision" entry locking the yq-from-manifest pattern as canonical for all future Phase 02 runbooks. ## Validation - `python3 jsonschema.validate(yaml.safe_load(modules/n8n-worker/module.yaml), schema)` → ✓ - All recovery commands manually traced for copy-paste correctness Operator feedback that triggered this fix: canary v3 ELEVATED defer + Pan Herbata-grindset calling out my "convert to open_loop" punt as escape from doing the actual fix. Co-authored-by: canary v3 reviewers (tech-claude, tech-gpt, product-gpt — caught 3 real bugs) Co-authored-by: Operator (called out the meta-vs-value drift) Co-authored-by: Claude Opus 4.7v4 — fix all 4 canary v3 findings (commit
0b0038f)Operator's feedback: stop punting to open_loop, fix the fucking bug.
tech-claudeHIGH: compose cwd wrongcd compose/apps/first (cite manifest field)tech-gpt+product-gptMEDIUM: truncated digestyq '.spec.runtime.image_observed' modules/n8n-worker/module.yaml— sources digest at exec time, no truncation possiblecodex techMEDIUM: bypasses platformctlplatformctl plan/apply --approved <SHA>per charter §3.docker composeis break-glass fallbackclaude techlow: schema format mismatchacknowledged_ataligned todate-timematchingimage_audit_tsPlus: removed "truncated digest" from OPEN_LOOPS (fixed not deferred), added "Runbook digest pattern" entry locking the
yqpattern for all future Phase 02 runbooks.Re-fire canary v4 starting.
3+3 ensemble review by
claude— tech + product hatsTech hat: ✅ OK (confidence 0.85)
Risks
low— yq portability risk in runbook recovery snippetmodules/n8n-worker/runbook.md —docker pull "$(yq '.spec.runtime.image_observed' modules/n8n-worker/module.yaml)"``yqis ambiguous: mikefarah/yq (Go) returns raw strings; kislyuk/yq (Python wrapper around jq) returns JSON-quoted strings, which would yield"n8nio/n8n:..."and fail docker pull withinvalid reference format. Either pin the binary (e.g.mikefarah/yq) or pipe throughtr -d '"'for safety. Break-glass-only path so non-blocking.Opportunities
data_classification: statelessas a new v2 field, but the diff actually addsstatefulness: statelessunder runtime (the enum fordata_classificationis[public, internal, private-pii, secrets]and would rejectstateless). The implementation is correct; the description is misleading for future reviewers reading the PR log. Worth correcting if the PR is amended.image_observedas a free-form string with format documented in description only. Apatternregex matching^[\w./-]+:[\w.-]+(@sha256:[a-f0-9]{64})?$would catch typos and truncated-digest pastes (the same failure mode operator already documented in OPEN_LOOPS canary v3). Optional, low-priority hardening.image_observed/image_digest_pinned_in_compose/image_audit_ts/image_buildbelong in generated observed-state inventory (.platform/state/modules/<id>.status.json) rather thanmodule.yaml(desired state). Accepting them here as a canary is reasonable — but the migration path should land before this pattern is repeated across many modules, otherwise scheProduct hat: ✅ OK (confidence 0.80)
Risks
low— Audit fields in desired-state manifest blur observed vs. desiredmodules/n8n-worker/module.yaml adds image_observed/image_audit_ts under spec.runtime; OPEN_LOOPS.md already flags this as architecturally belonging in observed-state inventoryOpportunities
3+3 ensemble review by
codex— tech + product hatsTech hat: ❌ NOT_OK (confidence 0.90)
Risks
medium— Canonical recovery command omits required apply plan filemodules/n8n-worker/runbook.md:57 saysplatformctl apply --approved, but control-plane/platformctl/cli.py:106 requires positionalplan_filebefore--approved.platformctl plan n8n-worker --out .plansthenplatformctl apply .plans/n8n-worker.plan.json --approved <merged-PR-SHA>; note--dry-runif this environment is still pre-Phase 05.Product hat: ✅ OK (confidence 0.78)
Risks
medium— Live audit facts can age into false confidencemodules/n8n-worker/runbook.md:46-53 marks release-readiness items based on a 2026-05-02 sample; schema/module.schema.json:216-220 says audits older than 30 days should be re-run, but no tooling flips or flags stale checklist state.Opportunities
3+3 ensemble review by
glm— tech + product hatsTech hat: ✅ OK (confidence 0.95)
Risks
medium— Runtime image drift tracking deferredschema/module.schema.json:207-229 + state/L3/OPEN_LOOPS.md:13-15.platform/state/modules/<id>.status.jsontooling exists per charter §2 layout.low— acknowledged_risks not used in this moduleschema/module.schema.json:302-322 vs modules/n8n-worker/module.yaml (empty)Opportunities
yq '.spec.runtime.image_observed'instead of copy-pasting truncated digests. OPEN_LOOPS.md captures this as v3 canary learning for Phase 02 consistency across modules.Product hat: ✅ OK (confidence 0.95)
Risks
low— Schema adds 'acknowledged_risks' complexityschema/module.schema.json (lines 302-321)Opportunities
Review decision
Status: ELEVATED — recommended action:
deferNo blockers or high-severity risks. See per-actor comments + local decision packet for medium/low details.
Reviewer dissents
tech-gptvoted NOT_OK (confidence 0.90)Operator decisions (yes/no)
Per-actor evidence: see comments by
claude,codex,glmabove. Tech: 2/3 OK · Product: 3/3 OK.Per canary v5 NOT_OK from tech-gpt (confidence 0.90): > "Canonical recovery command omits required apply plan file" > Evidence: control-plane/platformctl/cli.py:106 — plan_file is positional, required. Previous: `platformctl apply --approved <merged-PR-SHA>` (broken — missing positional) Now: explicit two-step `platformctl plan n8n-worker` then `platformctl apply .plans/n8n-worker.plan.json --approved <merged-PR-SHA>` Verified against control-plane/platformctl/cli.py:97-123 (cmd_plan + cmd_apply signatures). Risks 1+2 from canary v5 (1/6 low-priority each) accepted as already-mitigated: - Risk 1 "Runtime image drift tracking deferred" — already in OPEN_LOOPS as "Image audit fields long-term home" entry. - Risk 2 "Live audit facts can age into false confidence" — schema already documents "Stale audits (>30 days) should be re-run" on image_audit_ts. Co-authored-by: tech-gpt canary v5 reviewer (caught real bug) Co-authored-by: Claude Opus 4.73+3 ensemble review by
claude— tech + product hatsTech hat: ✅ OK (confidence 0.70)
Risks
medium— Schema validation never executed in verification — only YAML parse testedPR description verification:python3 -c "import yaml; yaml.safe_load(open('modules/n8n-worker/module.yaml'))". This only proves the YAML is syntactically valid, not that it conforms to schema/module.schema.json. The PR adds new fields underspec.intent(user_facing_outcome,acceptance_criteri`jsonschema -i modules/n8n-worker/module.yaml schema/module.schema.json(or equivalent platformctl validate). If intent schema lacks these fields, either add them to the schema in this PR or revert the YAML additions. One liner diff to verify additionalProperties posture onintentwould resolve this in 30s.low— image_audit_ts/acknowledged_at useformat: date-timewithout pattern fallbackschema/module.schema.json new fields use"format": "date-time"only. Per JSON Schema Draft 2020-12,formatis annotation-only by default; only the format-assertion vocabulary (or validator-specific assertion mode) enforces it. Malformed timestamps like '2026-05-02 09:11' (no T, no Z) would silen"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?(Z|[+-][0-9]{2}:[0-9]{2})$"to harden. Defer until next schema-touching PR if not blocking.Opportunities
docker compose up -d --no-deps n8n-workerassumes deps are healthy — runbook.md break-glass section runs with--no-deps, which is correct for worker-only restart but silent if postgres/redis are also down — operator gets a worker that comes up and immediately fails to dequeue. One-line caveat ('only run if postgres+redis are confirmed healthy viadocker ps') would prevent a confused 02:00 follow-up. Not blocking; runbook is otherwise solid.Product hat: ✅ OK (confidence 0.85)
Risks
low— Schema addsacknowledged_risksarray but PR doesn't use itschema/module.schema.json +14 lines foracknowledged_risksobject, but n8n-worker module.yaml has zero entries. First actual use will likely come in PR #27 or later canary.Opportunities
docs/templates/module-runbook.mdor a /skill) would let subsequent canaries copy structure verbatim instead of reinventing — directly saves operator attention and prevents drifimage_audit_tsstaleness visible inplatformctl status— Schema description says '>30 days should be re-run' but nothing surfaces this. metadata.next_review (2026-06-02) covers the cadence today, but as more modules adopt these fields, a single 'which audits are stale' query saves the operator from manually scanning N module.yaml files. Low priority — only worth doing once 3+ modules have the field.3+3 ensemble review by
codex— tech + product hatsTech hat: ✅ OK (confidence 0.84)
Opportunities
Product hat: ❌ NOT_OK (confidence 0.76)
Risks
medium— Do not merge temporary observed-state schema as reusable patternstate/L3/OPEN_LOOPS.md: Phase 02 audit fields are explicitly described as accepted temporarily in module.yaml but architecturally belonging in generated observed-state inventory; schema/module.schema.json then makes those fields first-class schema-supported manifest fields for all modules.Opportunities
3+3 ensemble review by
glm— tech + product hatsTech hat: ✅ OK (confidence 0.95)
Risks
low— Runbook path assumption depends on repo rootmodules/n8n-worker/runbook.md:66modules/n8n-worker/module.yamlworks from root but fails if runbook opened in isolation.Opportunities
spec.runtimeis pragmatic interim solution that doesn't block eventual migration.Product hat: ✅ OK (confidence 0.95)
Opportunities
OPEN_LOOPS.mdnote correctly flags thatimage_observedbelongs in observed-state inventory, not desired-statemodule.yaml. Consider adding a comment inmodule.schema.jsonlinking to this decision so future reviewers understand this is temporary Phase 02 scaffolding.platformctl apply) vs break-glass recovery. Good pattern. Ensureplatformctl planis mentioned at the top of recovery sections for all future Phase 02 runbooks for consistency.Review decision
Status: ELEVATED — recommended action:
deferNo blockers or high-severity risks. See per-actor comments + local decision packet for medium/low details.
Reviewer dissents
product-gptvoted NOT_OK (confidence 0.76)Operator decisions (yes/no)
Per-actor evidence: see comments by
claude,codex,glmabove. Tech: 3/3 OK · Product: 2/3 OK.Operator decisions applied (commit
8227191)Both R1 and R2 entries in
state/L3/OPEN_LOOPS.mdcarry operator's deeper platform-principle notes, not just "accepted":Per pre-auth A (operator 2026-05-02: "5 PR-y small, które każde z nich możesz sam zaakceptować"), with all 3 risks now resolved, claude self-merges this PR.
Wallclock retrospective: 6 canary iterations to land. n8n-worker ended up the canary-test mule for: schema-rejection bug, recovery-command syntax bugs, runbook drift, schema timestamp mismatch. Real value extracted from each iteration. Pattern locked for next 4 Phase 02 PR-y.
— pan-herbata