feat(provision): render + diff core (U10 U3) - #47
Conversation
U10 U2 (config-write engine opaque whole-file text format) merged (PR #45, fa9e4b2). START-HERE ▶ NEXT repointed to /unit-loop U3 or U4 (both parallel- eligible off merged U1); PRODUCT.md U10 row 1/7 → 2/7. No new decision fork (implementation unit; the byte-exact-noop + batch-guard learnings live in the compound doc; the 3 U10-wrap-up ledger rows wait for U7-complete).
Render copy, compose, scaffold, and config-merge entries into typed targets, then classify live state with semantic structured comparisons and byte-exact text checks. Preserve content-free failure results and scaffold drift semantics for the later plan, status, and apply surfaces.
📝 WalkthroughWalkthroughAdds deterministic blueprint rendering and rendered-versus-live diff planning for provisioning, with typed errors, bounded reads, structured parsing, byte comparisons, and comprehensive tests. Updates documentation to record U10 progress and revised next steps. ChangesProvisioning planning pipeline
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Manifest
participant renderBlueprint
participant RenderIo
participant diffRendered
participant DestinationReader
renderBlueprint->>Manifest: iterate provisioning entries
renderBlueprint->>RenderIo: read bounded sources and destinations
RenderIo-->>renderBlueprint: content or read status
renderBlueprint->>diffRendered: pass rendered files
diffRendered->>DestinationReader: read live destinations
DestinationReader-->>diffRendered: content, bytes, or read status
diffRendered-->>Manifest: return plan rows or typed error
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
Adds the pure “render + diff” core for the U10 provisioning engine: render transforms a blueprint manifest into per-destination rendered outputs, and diff turns rendered-vs-live into typed plan rows with presence semantics for structured configs and byte-exact comparison for opaque text.
Changes:
- Introduces
renderBlueprintwith support forcopy,compose,scaffold, andconfig-mergetransforms, producing deterministic, content-free typed errors on failures. - Introduces
diffRenderedto compute per-destination plan actions (create/noop/overwrite/merge/scaffold-skip) with semantic comparisons for JSON/TOML/YAML and byte-exact comparisons fortext. - Adds comprehensive test coverage for determinism, byte-identity behavior, presence semantics, and content-free failure modes; updates product/status docs.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/provision-render.test.ts | Adds coverage for render + diff behavior, determinism, and failure modes. |
| src/provision/render.ts | Implements pure render core for blueprint transforms with typed results. |
| src/provision/diff.ts | Implements pure diff core producing plan rows with semantic/byte-exact comparisons. |
| docs/START-HERE.md | Updates “NEXT” guidance for U10 progression notes. |
| docs/PRODUCT.md | Updates product status to reflect recently merged U10 sub-units. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| **Still to build:** | ||
| - **U10 (building — 1 of 7 sub-units in) — project provisioning:** a project carries one versioned "blueprint" (which roles run in which harness, on which model, with which files), and agent-os pushes it into Claude Code, Codex, and Cursor natively — reversibly, with a dry-run preview and a drift report. First sub-unit shipped (PR #44): the typed manifest contract + a pure/total loader + the shared **front-gate** that certifies a blueprint (valid schema, no secrets, no machine-specific paths) before any provisioning verb runs. Rescoped from the earlier "parity actions" framing by lived dogfood evidence (decision #52). | ||
| - **U10 (building — 1 of 7 sub-units in) — project provisioning:** a project carries one versioned "blueprint" (which roles run in which harness, on which model, with which files), and agent-os pushes it into Claude Code, Codex, and Cursor natively — reversibly, with a dry-run preview and a drift report. Two sub-units shipped: the typed manifest contract + a pure/total loader + the shared **front-gate** that certifies a blueprint (valid schema, no secrets, no machine-specific paths) before any provisioning verb runs (PR #44), and the config-write engine's opaque whole-file `text` write primitive the copy/compose transforms need — verbatim, byte-identical, on the same backup/atomic-write/undo discipline as structured configs (PR #45). Rescoped from the earlier "parity actions" framing by lived dogfood evidence (decision #52). |
| - ✅ **U10 U1 — manifest contract + blueprint loader + front-gate SHIPPED + MERGED** (PR #44, merge `e0d3b3a`, 2026-07-21; decisions **#53–#54**). The typed blueprint the Act half (U2–U7) inherits: a discriminated-union `Manifest` schema in the contract seam (KTD10) + a **pure/total loader** + the shared **front-gate** every verb runs (KTD5: version-compat → cardinality preflight → strict parse → secret + machine-abspath scan → typed `BlueprintLoad`, never throws; bounded reads mirror `src/scan`/`src/codex-credential`). Full loop: ce-work (opus subagent) → simplify → **ce-code-review (6 reviewers incl. the redaction-boundary-reviewer)** → **Codex gate 4 rounds + a logged Option-B marker advance** (companion hung twice mid-session; the only un-gated delta was the path-containment fold = codex's own round-4 recommendation, Jarod-authorized). **Two proof-first bug fixes** — JSON-escaped-secret **R4 gate bypass** (empirically reproduced by the redaction reviewer: raw-byte scan misses `\uXXXX`, `JSON.parse` decodes it into `loaded.manifest` → now scans raw **and** normalized forms) and **`schemaVersion≤0` misroute**. **Folds**: manifest cardinality preflight (no-hang) + schema-level path containment (R1/R9, POSIX-only). **Reverted**: the classifier ReDoS `{0,64}` quantizer-bound (regressive false-negative + whack-a-mole) → linear-scanner **#42**; config-source effective-form scan (copy+merge) → **#43** (owner U5/apply). Bots folded (doc accuracy) or declined-with-rationale (Windows-path + symlink, both #45-scoped). **527 tests green, tsc clean.** Learning compounded → `docs/solutions/architecture-patterns/content-safety-gate-scan-effective-form-bound-the-work.md`; `CONCEPTS.md` gains **Front-gate**. | ||
| - ⏳ **▶ NEXT — `/unit-loop U2`** of the [U10 plan](plans/2026-07-20-003-feat-u10-provisioning-engine-plan.md), **in a FRESH session** — the codex companion hung twice this session; the adversarial gate needs a clean CC session (2 hangs, 0 output; a CLI re-login won't clear it). **U2, U3, U4 are parallel-eligible** (each depends only on the now-merged U1 — run any, or parallelize), then U5 (needs U2–U4), then U6 ∥ U7. Run each unit's gate with the decision-#45 threat-model pointer, and **keep backticks out of the gate focus text** (they command-substitute in the wrapper's shell and wedge codex — cost 2h+ this session). Don't re-litigate U1's rulings: **POSIX-only paths**; **R4 secret-detection is best-effort per the plan's own spec** (completeness tracked in **#42** + **#43**); the `Manifest` schema is contract-frozen at `schemaVersion 1`. **U15** (launchd always-on) follows; the **MVP-definition session** lands before/at **U11**. Deferred, unchanged: handoff **Phase 3** (→ `~/.claude`); the ClaudeOS delta walkthrough; the feature-catalog `Call` pass; **the CE salience-layer investigation** (memory-captured 2026-07-21 — the checkpoint rule loads but doesn't fire mid-task; wants an active hook trigger). | ||
| - ✅ **U10 U2 — config-write engine opaque whole-file `text` format SHIPPED + MERGED** (PR #45, merge `fa9e4b2`, 2026-07-21). The whole-file write primitive U10's copy/compose transforms need (R7/KTD1/KTD2): a 4th `ConfigFormat` `text` + **`writeTextFile`** (verbatim, byte-identical, format forced) on the shared `publish()` core, an `allowText` guard keeping `text` off the merge/removal paths, and optional recorded-only `batchId`/`projectRoot` on `UndoEntry`/`MergeOptions` (U5 batch-undo consumer). Full loop: ce-work → simplify (**0 findings**) → **ce-code-review (10-persona full roster; 9 clean + 1 P2 header doc-drift folded)** → **Codex gate — no-ship #1 caught 2 real findings** (batch write/read schema asymmetry made a partial/non-string batch value applied-but-un-undoable → **fail-closed both-or-neither guard**; a lossy-utf8 no-op broke byte-identity → **byte-exact `text` compare**; re-run **APPROVED**) → **CodeRabbit (Major: guard now rejects truthy non-strings) + Copilot (×3 doc rewording) folded**. **546 tests green, tsc clean.** Learning compounded → `docs/solutions/architecture-patterns/byte-identity-contract-needs-byte-exact-noop-not-just-verbatim-serialize.md` (a byte-identity contract must be byte-exact on BOTH the serialize AND the no-op-read sides — the *inverse* of presence-semantics on the same no-op surface; both surfaced by the adversarial gate escalating an in-process residual). | ||
| - ⏳ **▶ NEXT — `/unit-loop U3`** (render + diff) **or `/unit-loop U4`** (target registry + Cursor scanner row) of the [U10 plan](plans/2026-07-20-003-feat-u10-provisioning-engine-plan.md), **in a FRESH session**. **U3 and U4 are both parallel-eligible** — each depends only on the now-merged U1 (U2 is merged too); run either, or parallelize. Then **U5** (needs U2–U4), then **U6 ∥ U7**. Run each unit's gate with the decision-#45 threat-model pointer, and **keep backticks AND parens out of the gate focus text** (they command-substitute in the wrapper's shell). Don't re-litigate settled rulings: **POSIX-only paths**; **R4 secret-detection is best-effort per the plan spec** (completeness → **#42** + **#43**); the `Manifest` schema is contract-frozen at `schemaVersion 1`; the `text` format's byte-identity + batch-field guards are settled. **U4 execution note:** re-verify Cursor's `.cursor/agents` / `.cursor/skills` / `.cursor/mcp.json` shapes against a live install + current docs before encoding descriptors (run-1 evidence is days old; a mismatch is a stop condition). **U10 wrap-up (at U7 complete, not before):** log the 3 decision-ledger rows — Cursor roster (amends #45), trigger surface (supersedes KTD7), `RUN_STATE.md` naming. **U15** (launchd always-on) follows; the **MVP-definition session** lands before/at **U11**. Deferred, unchanged: handoff **Phase 3** (→ `~/.claude`); the ClaudeOS delta walkthrough; the feature-catalog `Call` pass; **the CE salience-layer investigation** (checkpoint rule loads but doesn't fire mid-task; wants an active hook trigger). |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/PRODUCT.md`:
- Line 6: Advance the canonical U10 progress snapshot from 2/7 to 3/7: in
docs/PRODUCT.md lines 6 and 119 update the counts, change the next range/action
to U4, and in line 51 add the render/diff sub-unit to the shipped list; in
docs/START-HERE.md line 61 make U4 the next action for fresh sessions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2d1f884f-01d7-485a-b41e-0b3c5f65373e
📒 Files selected for processing (5)
docs/PRODUCT.mddocs/START-HERE.mdsrc/provision/diff.tssrc/provision/render.tstests/provision-render.test.ts
| > **Freshness rule:** `/handoff` updates this page whenever something ships. If this page and reality ever disagree, that's a bug — flag it. | ||
|
|
||
| _Last updated: 2026-07-21 · Status: **v0.1 "Continuity" in progress — 11 of 15 units shipped**; latest: **U10 BUILDING** — the provisioning engine's first sub-unit (the manifest contract + blueprint loader + shared front-gate) shipped + merged (PR #44, decisions #53–#54). Next: **U10 sub-units U2–U4** (parallel-eligible). · Roadmap postures locked by the 2026-07-10 interview (decisions #32–#41)_ | ||
| _Last updated: 2026-07-21 · Status: **v0.1 "Continuity" in progress — 11 of 15 units shipped**; latest: **U10 BUILDING** — 2 of 7 provisioning sub-units merged: the manifest contract + loader + shared front-gate (PR #44, decisions #53–#54), and the config-write engine's opaque whole-file `text` write primitive (PR #45). Next: **U10 sub-units U3–U4** (parallel-eligible). · Roadmap postures locked by the 2026-07-10 interview (decisions #32–#41)_ |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Advance the canonical U10 progress snapshot after this PR.
The PR ships U3 render/diff, but both continuity documents still describe U10 as 2/7 and U3 as upcoming.
docs/PRODUCT.md#L6-L6: update the top status and next range to 3/7 and U4.docs/PRODUCT.md#L51-L51: add render/diff to the shipped U10 sub-units.docs/PRODUCT.md#L119-L119: update the decoder ring from 2/7 to 3/7.docs/START-HERE.md#L61-L61: make U4 the next action for fresh sessions.
📍 Affects 2 files
docs/PRODUCT.md#L6-L6(this comment)docs/PRODUCT.md#L51-L51docs/PRODUCT.md#L119-L119docs/START-HERE.md#L61-L61
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/PRODUCT.md` at line 6, Advance the canonical U10 progress snapshot from
2/7 to 3/7: in docs/PRODUCT.md lines 6 and 119 update the counts, change the
next range/action to U4, and in line 51 add the render/diff sub-unit to the
shipped list; in docs/START-HERE.md line 61 make U4 the next action for fresh
sessions.
Bots (Copilot + CodeRabbit) on PR #46/#47 caught two continuity-doc nits in main (not in the U3/U4 diffs): - PRODUCT.md:51 still read '1 of 7 sub-units in' — a leftover from the U2 handoff where the other two counts were updated to 2/7. Now consistent. - START-HERE ▶ NEXT gate note said parens 'command-substitute'; they actually glob-fail in zsh (backticks/$() command-substitute). Wording corrected. The U3/U4 CODE drew zero bot findings.
|
Bot review triaged — the U3/U4 code drew zero findings from either bot. All flagged items were continuity-doc nits in
No changes to this branch (the reviewed code is clean). Ready to merge. |
U10 U3 — Render + diff core
Part of the U10 provisioning engine plan. The pure core of the Act half: transforms render a blueprint into per-target content, and diff turns rendered-vs-live into typed plan rows (R5, R6, R8 read-side; KTD3/KTD6/KTD8).
What changed
src/provision/render.ts— renderers per transform (copy = source bytes; compose = deterministic ordered assembly + versioned boilerplate; scaffold = content only when destination absent; config-merge = a patch handed to the U14 merge path).src/provision/diff.ts— per-file rows{role, harness, destination, action, drift}: semantic comparison on parsed values for config formats (presence semantics — a foreign-formatted live file that parses equal is a no-op), byte-exact fortext(Buffer.equalson raw destination bytes — inherits U2's byte-identity discipline), scaffold excluded from drift.Verification
bun test566 pass / 0 fail,tscclean.Provenance & review (dogfood: Codex-executes / Claude-reviews)
Built by Codex (
gpt-5.6-sol xhigh) viace-work(incl.ce-simplify-code+ce-code-review) in an isolated worktree, then reviewed cross-model by Claude as the architect gate. The cross-model adversarial pass ran during the build (host Codex → peer Claude,opus-4-8,independence_verified) — verified on disk. One P2/conf-50 peer finding (foreign-config parse abort) was reasoned through: copy/compose degrade to overwrite on a malformed live target; config-merge stays strict (needs a valid base — matches AE4's "no partial ambiguity").Deferred (tracked, not blockers)
computeDiffis all-or-nothing on an incompatible target; per-role degradation vs abort-all is astatus/applyUX decision → U5/U6.🤖 Built by Codex, reviewed by Claude Code
Summary by CodeRabbit
New Features
Tests
Documentation