Harness Intelligence Wiki
SpecsCLIIP-318 Govern Project Settings and Scaffold State Safely

Implementation Notes

Implementation Notes

Summary

  • T1 established the CLI-owned project-settings boundary for IP-327.
  • The boundary decodes current and legacy settings, reports safe upgrades, rejects missing/invalid/ambiguous state with typed errors, and never writes during reads.
  • Initialization and discriminated writes enforce the three ownership classes while preserving unknown fields.
  • Independent-review repairs prove legacy writes do not materialize inferred fields, Git config inference observes every repository-manager remote, and the live filesystem distinguishes only ENOENT from typed access failures.
  • T1/T2 targeted readonly rereview is complete; the combined focused lifecycle suite passes 131/131 tests and CLI typecheck passes.
  • T3 added one pure deterministic desired-state compiler for IP-328. Its public result models managed files, dependencies, structured fields/keys, projections, provenance, degradation, omission, and projection failure without receipt or runtime authority.
  • T4 routes init, check, and update through that shared desired state while keeping observation and application outside the compiler. All focused, lifecycle, public/projection, type, build, format, diff, and independent-rereview gates pass.
  • T5 adds pure deterministic reconciliation for managed files, dependencies, and structured keys. Update stages the plan but does not apply it or advance receipts.
  • T6 applies independent safe actions with typed outcomes, refreshes dependency lockfiles through an injected package-manager process boundary, and advances only truthful receipt state in a final persistence step.
  • T7 validates T1-T6 through packaged hi/hint only, adds no product or test surface, and leaves the canonical repository unchanged.
  • T8 routes the implemented lifecycle into the operator runbook and canonical CLI flow pages, closes independent review, synchronizes Linear evidence, and verifies the draft PR/stack gate. The existing pages were bounded factual patches, so the public writer fragment/beat/shape stages were intentionally skipped.

Execution Mode

  • Sequential implementation workers; T1 followed by T2 followed by T3 followed by T4 followed by T5 followed by T6. T7 was validation-only. T4-T7 landed in the convergence worktree for draft PR #87; original IP-318 draft PR #80 remains a sibling source PR.

Stack and Draft PR Checkpoint

  • Live GitHub evidence on 2026-07-20 shows PR #76 at 71af6c8249ea05ec46519471bc56da772f80d061, draft/open/unmerged, based on main.
  • PRs #77 through #83 and #87 are draft/open/unmerged sibling children based directly on team/stefan/refactor-cli-architecture: #77 facf1ba10a1caec39e1602dfb23e28c0e1669a03; #78 7b14739996d5b297088f0ff102bebb6e3c8f7eb7; #79 5c96dfd109a60285dca77ceb8093367e23e33356; #80 b302005fb55791b6225a946e54931ab6638a33cc; #81 fc30dd72954f080c66c85cd892a66a964d950169; #82 f24dad1893e14a856d6aef7b86e438aabad83c8b; #83 b487fd83dbbf296132310be3bb1dab0041fc6c40; #87 d129c9ffabd3f43f70fbefd751d14520c16d7b18.
  • The restored stack status showed that exact fan-out. The post-restoration and post-#87-update stack sync --dry-run previews reported Stack is current, so no further sync ran from those previews.
  • No stack track, PR-base retarget, manual rebase/force-push, sibling linearization, merge, or deployment was used for this checkpoint. In particular, #83 is a sibling and is not assumed to contain #77 through #82.
  • At tracker sync, PR #87 head was adec65792416c561857623b0978b07e8929f7c72, still open/draft/unmerged and based on team/stefan/refactor-cli-architecture. The required post-push stack sync --dry-run reported Stack is current.

Tracker Closeout

  • IP-328 moved to Done at 2026-07-20T01:02:45Z; evidence comment 0697ea59-892c-42c9-b303-17ad87a48d99.
  • IP-329 moved to Done at 2026-07-20T01:02:51Z; evidence comment 033824f7-107c-4239-a5c6-a236bc8bd751.
  • IP-318 moved to Done at 2026-07-20T01:02:52Z; evidence comment 39c3facb-2696-4ac8-8721-ce2beb5c0a8a.

Deviations From the Plan

  • None for T1-T6. T2 moved every lifecycle settings mutation to ProjectSettings; the synchronous helpers remain only for the separate tools command and compatibility tests, not init/ensure/check/scaffold/update.
  • T7's first init attempt submitted an empty required backlog URL; the CLI rejected it before writes and the same still-empty run root was reused. Direct update could not infer a missing repository manager from the isolated Git remote, so the manual flow restored the already-ensured gitlab choice after read-only hi check had proven the safe migration. hi ensure intentionally changed settings only; the run-owned wiki provider marker was reconciled separately before the final clean hi check.
  • The default-concurrency full CLI command twice exposed an existing cross-file temp cleanup race: another worker removed a pre-existing hi-projection-plan-* directory between snapshot and assertion. The exact assertion passed alone, and the full suite passed 446/446 with one worker. T7 did not change production or test code.
  • T8 changed documentation and evidence only. The writer flow was skipped because it corrected existing lifecycle pages from shipped contracts and accepted evidence rather than creating a new public guide or performing a major rewrite.

Review and Repair Ledger

  • T1 review repaired inferred-field materialization during legacy writes, incomplete Git-remote inference, and filesystem reads that collapsed access failures into absence. T1/T2 readonly rereview accepted the corrected lifecycle authority with no residual HIGH/MEDIUM finding.
  • T3 review REDs exposed non-canonical projection-summary ordering and dotted structured-path identity collisions. Repairs use locale-free ordering and segment-preserving identities.
  • T4 review REDs exposed ambient projection-plan inheritance, run-owned temporary-plan leakage, permissive explicit-plan validation, malformed-context fallback, and false duplicate rejection for legitimate cross-kind trace reuse. Final rereview reported no residual HIGH/MEDIUM finding.
  • T5's first review reported three HIGH and two MEDIUM ownership/planning defects. The next rereview reported four MEDIUM defects covering desired duplicate equivalence, symlink mode evidence, cross-family overlap, and duplicate invalid-observation determinism. The final repair added full-payload duplicate comparison, exact type-aware modes, cross-family failures, and deterministic invalid evidence; the exact gate passed 78/78.
  • T6 review REDs covered mode-mismatch authorization, missing receipt-only dependency/key observation, premature receipt staging, aggregate-layer exit mutation, absent lockfile refresh, dangling-symlink hashing, and unsafe family correlation. Repairs passed the exact 97/97 gate plus the full serial regression suite.
  • T7 added no implementation finding. Its only deviation was the existing default-concurrency temporary-directory race recorded above; isolated proof and the serial full suite passed.

T7 Packaged Lifecycle Acceptance

  • Run root /tmp/ip318-t7.sbgydH isolated HOME, TMPDIR, XDG cache/config/data, and global/system Git configuration. It contained no secrets and was the only manual resource removed.
  • Packaged bundled hi init created GitHub backlog/asset/repository settings at version 2.6.2. Packaged hi ensure changed them to Linear backlog, GitLab assets/repository, the Linear project URL, and the corresponding glab tool while preserving managed versions.
  • A legacy settings document without repository manager or required tools was checked read-only after the isolated repository gained a GitLab origin. hi check --json returned SafeSettingsUpgrade for exactly those fields; settings SHA-256 stayed 447fa84d… before and after.
  • Preview classified a user-edited managed hook and modified receipt-only tooling.json key as separate ScaffoldConflictPreserved outcomes. Write returned exit 1, applied: true, partial, and receiptPersisted: true, preserved both user values, and completed 30 independent actions.
  • Receipt evidence remained truthful: hook actual hash 29aaba45… retained prior receipt hash 6fd24a7c…; tooling actual value false retained receipt value true; updated lint actual and receipt hashes both became 3ccb6881…; a safely removed Claude agent was absent from disk and receipt.
  • Retry returned the same two conflicts with zero successes. Manifest 904f96b4…, hook 29aaba45…, and tooling 7cd62f60… hashes were unchanged. After resolving only the run-owned conflicts, structured-key removal succeeded and its receipt disappeared.
  • Packaged hi and hint both reported 2.6.2. Final hi check --json and hint update --check --json exited zero with current settings and no changes. Final settings/manifest hashes were 2a7935dc… and 58685ce5….
  • Canonical repository fingerprints were identical before and after: HEAD a2f65aea…, tree 1a3b5680…, and empty diff SHA-256 e3b0c442…. The temporary root was removed and its absence confirmed.

T6 Truthful Application and Receipts

  • applyScaffoldReconciliation executes actions in deterministic family/identity order, captures success or failure per action, and continues independent safe work.
  • A pure mapped-family fold combines the prior receipt with unchanged evidence and successful actions. Conflicts, planning failures, and application failures never advance ownership.
  • Update observes receipt-only dependencies and structured keys, so safe stale entries are removed while user-modified values remain conflicts with retained receipt evidence.
  • Dependency mutations refresh the detected Bun, pnpm, npm, or Yarn lockfile through an injected process adapter. Configured Yarn 1 and unconfigured # yarn lockfile v1 repositories use noninteractive, scripts-disabled Classic install flags; modern Yarn uses Berry's update-lockfile mode. A process failure restores the prior package manifest and every recognized lockfile snapshot, keeps the prior receipt, and leaves the action detectable so a successful retry reruns the process before advancing ownership.
  • The staged manifest preserves prior receipt fields, and receipt persistence runs last. The injected pre-write rejection test keeps the prior on-disk receipt intact without rolling back independent successful actions. The default direct manifest write is not atomic; a low-level write failure can require receipt inspection or repair.
  • File receipt matching requires exact content, type, and type-aware mode evidence. Dangling symlinks hash through lstat/readlink, persist a non-empty receipt hash, and retry cleanly.
  • runUpdate returns status as data. update-command owns nonzero exit selection, and the shipped hi update --json --write witness proves partial outcome serialization and exit 1.

T5 Pure Reconciliation Planning

  • planScaffoldReconciliation keeps desired state, observed repository state, and prior successful receipt evidence distinct.
  • One deterministic policy plans per-entry file, dependency, and structured-key creates, updates, removals, unchanged skips, user-modification conflicts, stale-ownership conflicts, invalid-observation failures, and known degradation/omission evidence.
  • A conflict or invalid observation suppresses only its own unsafe action; independent safe actions remain in the plan. Structured identities encode the file, entry kind, and full nested path, so unrelated keys remain outside the action.
  • Review repairs make tuple identities collision-free, compare full desired action payloads before deterministic duplicate collapse, reject duplicate desired/observed/receipt/invalid-observation identities, and surface desired projection failures.
  • Structured observation treats traversal through a primitive or array as invalid instead of missing. Whole-file ownership cannot overlap structured keys or package dependencies. Exact duplicate desired entries deduplicate; unequal values or metadata become scoped planning failures instead of last-write-wins replacement.
  • Receipt mode evidence is type-aware: regular files require the exact numeric mode, while symlinks require exact null mode evidence.
  • The update observer reports malformed dependency and structured-config values as typed invalid entries. At the T5 checkpoint, existing manifest data adapted only successfully managed file receipts because dependency and structured-entry receipt fields had not landed yet; T6 adds and consumes those fields.
  • Update computes and carries the plan without consuming its actions. T6 owns application outcomes and truthful receipt advancement.

T4 Shared Lifecycle Stages

  • compileScaffoldLifecycle owns the shared presentation-neutral lifecycle result. Equivalent init, check, and update inputs expose the same desired state while preserving distinct apply, compare, and reconcile stages.
  • Init applies desired output from missing managed state. Check observes and compares without a write-capable adapter or writable temporary directory. Update stages the same desired state before its existing reconciliation path.
  • Desired output carries the compiled context plan and projection evidence explicitly. The T3 compiler still has no filesystem, environment, process, receipt, observation, conflict-policy, or application input.
  • Projection subprocesses clear ambient DP_HARNESS_PROJECTION_PLAN. When a caller supplies a plan, it is serialized into a run-owned temporary directory, passed explicitly, validated against canonical context, and cleaned on success or failure.
  • Explicit plans reject malformed canonical context, unknown operations, unsafe paths, mismatched action traces, inconsistent summary traces, and invalid outcome tags. They never fall back to implicit context projection.
  • Projection summaries may legitimately share one trace identity across different contribution kinds. Validation matches the full trace semantics instead of treating that cross-kind reuse as a duplicate conflict.
  • Future-use hook: lifecycle or projection changes should consult this section and rerun the three T4 gates below before changing compiler inputs, process environment handling, explicit-plan validation, or temporary-plan ownership.

T3 Deterministic Desired State

  • compileDesiredScaffoldState accepts only ContextPlan, validated ProjectSettingsValue, baseline/catalog data, and explicit IP-326 capability/projection results.
  • The compiler canonicalizes scopes, packs, contributions, compatibility, references, required tools, files, dependencies, structured entries, capability rows, projection order, and action order. Equivalent semantic values produce one stable public result independently of input object identity and caller label.
  • Baseline files, package dependency entries, structured field/key ownership, and harness actions retain explicit provenance and identities for later observation/reconciliation work.
  • Degraded applied projections, known omissions, and projection failures remain distinct evidence. Duplicate managed identities return DesiredScaffoldStateCompilationFailure instead of silently applying last-write-wins behavior.
  • Review repairs canonicalize every projection summary array and its derived evidence with locale-free ordering. Structured path identities preserve segment boundaries, including dotted segments.
  • Receipt, repository state, filesystem, environment, process, conflict policy, and application state are absent from the input type. T3 performs no effects or adapter calls.
  • T3 does not migrate init/check/update. T4 owns lifecycle staging, and later IP-320 convergence will migrate the aggregate runtime to the shared seam.

T2 Lifecycle Authority

  • Root hi init owns first settings initialization and wiki/onboarding seed output.
  • Root hi scaffold retains repo-aware setup. It preserves user choices and unknown fields while updating only calculated tools and managed versions.
  • hi ensure updates user choices and reconciled tools without changing managed versions or running scaffold behavior.
  • hi check reads upgrade assessment through the authority and never writes settings.
  • hi update preserves user choices and unknown fields, computes expected settings bytes in its temporary tree through calculated-tool and managed-version authority changes, and applies only the resulting settings fingerprint. Missing legacy settings are inferred through the authority before initialization; conflicting hints remain typed validation failures.
  • Current command help, generated init handoff copy, report examples, executable fixtures, CLI README, and normative repository docs use only hi init and hi scaffold. Canonical shared skills, apps/cli/skills mirrors, and bundled baselines were explicitly excluded by user instruction; their stale nested-route guidance remains known and was not investigated or synchronized. IP-328 desired-state/reconciliation work remains untouched.

Surprises and Decisions

  • A present requiredTools array can still require a safe upgrade when it omits the selected repository manager's required CLI. Upgrade assessment compares canonical derived tools, not field presence alone.
  • Legacy provider evidence that cannot determine a repository manager is an AmbiguousSettingsUpgrade, not invalid or missing settings.
  • Writer output starts from the parsed raw document. Validated/inferred settings inform calculations and the returned read result but are not merged into another ownership class's persistence request.
  • User-choice writes validate after applying their authoritative fields, so they can repair a legacy missing repository manager without guessing or materializing calculated/managed fields. Other writers still require readable, unambiguous settings before persistence.
  • Git config contributes zero, one, or many repository-manager hints. Zero remains unresolved, one is a safe inference, and many return typed evidence containing repository-manager IDs such as gitlab and bitbucket.
  • The live read adapter attempts the read directly. ENOENT becomes absent; permission and other read errors become ProjectSettingsAccessFailure.
  • The public setup contract does not persist project settings or expose the raw typed projection inputs. Its T3 fixture is explicitly compiler-only evidence and supplies validated settings directly; representative partial/failure projection integration lives in compile.test.ts through the public compiler seam.
  • Environment isolation belongs at the process boundary. Omitting an environment override is different from explicitly deleting it; the runtime helper removes keys whose supplied value is undefined before spawning the child.
  • Projection-plan cleanup is ownership-scoped. The caller removes only the temporary directory it created and leaves unrelated temporary entries untouched.

Sanity Checks

CheckResultNotes
bun run --cwd apps/cli test -- src/features/project-settings/project-settings.test.ts src/core/tools.test.tsPassed26 tests across the public feature, live adapter, and core compatibility surface
bun run --cwd apps/cli check-typesPassedNo TypeScript errors
bun run --cwd apps/cli buildPassedPackaged CLI bundled 1,039 modules
Complete T2 lifecycle suitePassed131/131 tests across 11 settings, lifecycle, generated-copy, context, and executable public-output files
Targeted oxfmt --checkPassedAll nine owned implementation/evidence files are formatted
Targeted oxlintBlockedExisting Effect plugin load fails because @typescript-eslint/utils is absent from the installed workspace graph
autoreview --mode localBlockedInstalled Codex CLI rejects configured gpt-5.6-sol and requires a newer CLI
Targeted readonly rereviewPassedCorrected topology and completed T1/T2 evidence accepted
T3 exact REDPassedReview regressions failed 2/26 for summary ordering and dotted path-segment identity; public context stayed green
T3 exact GREENPassedCompiler plus public context passed 26/26 tests; CLI tsc --noEmit passed
T3 focused format/diffPassedOxfmt and git diff --check passed all nine owned implementation, fixture, and evidence files
T4 focused suitePassedbun run --cwd apps/cli test -- src/runtime/scripts.test.ts src/scaffold/output.test.ts src/data/scripts/sync-subagents.test.ts passed 48/48 across 3 files
T4 exact lifecycle suitePassedbun run --cwd apps/cli test -- src/features/scaffold-state/lifecycle.test.ts src/scaffold/stage.test.ts src/scaffold/run.test.ts src/cli/check-command.test.ts src/update/run.test.ts passed 121/121 across 5 files
T4 public/projection suitePassedbun run --cwd apps/cli test -- src/cli/check-command.test.ts src/features/scaffold-state/lifecycle.test.ts src/features/scaffold-state/compile.test.ts src/features/scaffold-state/desired-output.test.ts src/scaffold/output.test.ts src/context/public-context-contract.test.ts src/cli/public-output-contract.test.ts src/data/scripts/harness-projection/contract.test.ts src/data/scripts/sync-subagents.test.ts passed 122/122 across 9 files
T4 static and build gatesPassedbun run --cwd apps/cli check-types, bun run --cwd apps/cli build, focused Oxfmt across all 29 implementation files, and git diff --check passed
T4 independent rereviewPassedPASS — no residual HIGH/MEDIUM findings. Reviewer validation passed 66 tests across 4 files; the readonly reviewer made no edits
T5 exact reconciliation suitePassedbun run --cwd apps/cli test -- src/features/scaffold-state/reconcile.test.ts src/update/run.test.ts passed 78/78 across 2 files
T5 regression suitesPassedT1/T2 passed 65/65, T3 passed 94/94, T4 passed 48/48, lifecycle passed 59/59, and packages/scaffold passed 47/47
T5 static and build gatesPassedCLI and packages/scaffold typechecks passed; CLI build bundled 1,056 modules
T6 exact application suitePassedExact four-file gate passes 97/97; full CLI passes 446/446 serially; CLI build bundles 1,058 modules and CLI typecheck passes
T7 packaged lifecyclePassedIsolated packaged init/ensure/check/update preview/apply/retry/final-clean flow proved settings ownership, two scoped conflicts, 30 independent successes, truthful receipt retention/removal, both aliases, unchanged canonical fingerprints, and owned cleanup
T7 exact gatesPassedBuild bundled 1,058 modules; CLI typecheck, root behavior 4/4, and diff check passed; full CLI passed 446/446 with one worker after the existing concurrency-sensitive temp assertion passed 1/1 alone
T8 wiki-safe gatePassedWiki tests passed 6/6; direct fumadocs-mdx, next typegen, and native TypeScript checks passed without running the mutating sync wrapper
T8 CLI/root gatePassedFull CLI passed 446/446 with one worker; build bundled 1,058 modules; CLI typecheck and root behavior 4/4 passed
T8 sync preflightBlockednode apps/wiki/scripts/sync-content.mjs --check reported existing project-owned routing drift that would delete canonical routed specs and update projected docs. The command made no writes; the mutating wrapper was intentionally skipped
T8 focused format/diffPassedExact targeted Oxfmt and scoped git diff --check passed all 13 T8 paths
T8 first autoreviewRepairedAccepted three P2 findings: replace the mutating recorded wiki command, remove stale nested/alias command names, and qualify non-atomic receipt-write durability
T8 second autoreviewRepairedAccepted the stale manifest pack-drift invariant and routed runbook findings. The manifest now matches automatic detected-pack adoption, and explicit ownership expansion allowed the routed runbook to be refreshed from its source without applying unrelated sync drift
T8 third autoreviewRepairedAccepted the remaining finalPacks wording contradiction. The manifest now treats recorded packs as prior accepted evidence augmented by current default/detected resolution in write mode
T8 fourth autoreviewRepairedAccepted the stale baseline environment variable and overbroad recorded-extra-pack classification. Source and routed runbooks now use DP_BASELINE and retain recorded extras as prior accepted selections pending explicit removal
T8 parent independent reviewRepairedAccepted two MEDIUM stale environment-name findings. Source and routed runbooks now document DP_SCRIPT_RUNTIME; root docs use the shipped HI_NO_UPDATE_CHECK, HI_NO_SKILL_UPDATE_CHECK, HI_UPDATE_TAG, and HI_STARTUP_CHECK_INTERVAL_MS startup variables

Acceptance Criteria Status

CriterionStatusNotes
Shared validated settings componentMet for T1Public Effect service owns decode, assessment, initialization, and ownership-scoped mutation
Safe legacy upgrades preserve user choicesMet for T1Tests cover safe conversion, ambiguous evidence, no read writes, and unknown-field preservation
Three authoritative field classesMet for T1Raw-document regressions prove each writer changes only its class and user choices can repair their own ambiguity
Every lifecycle command uses the boundaryMet for T2Init, ensure, check, scaffold, and update use explicit authority reads/initialization/ownership-scoped writes
One deterministic desired stateMet for T3Pure public compiler owns canonical desired state and explicit IP-326 capability/projection evidence
IP-328 lifecycle integrationMet for T4Init/check/update share desired state through distinct apply/compare/reconcile stages
IP-329 reconciliationMet for T6Scoped planning, mixed application outcomes, lockfile refresh, and truthful final receipt advancement are complete

Manual Review Checklist

AreaCheckHow to performExpected result
Project settings boundaryRe-run focused public contractsbun run --cwd apps/cli test -- src/features/project-settings/project-settings.test.ts src/core/tools.test.tsCurrent/missing/invalid/safe/ambiguous reads, legacy writer permissions, multi-remote inference, adapter failures, preservation, and real temp cleanup all pass
Static contractRe-run CLI type validationbun run --cwd apps/cli check-typesCommand exits zero
Desired stateRe-run T3 public contractsbun run --cwd apps/cli test -- src/features/scaffold-state/compile.test.ts src/context/public-context-contract.test.tsIdentical semantic inputs compile identically; projection degradation/omission remains explicit; public fixture stays exact
Lifecycle stagingRe-run focused T4 contractsRun the focused, exact lifecycle, and public/projection commands in the T4 sanity-check rows aboveInit/check/update share desired state; check stays effect-free; projection plan validation and owned cleanup remain strict
Truthful applicationRe-run exact T6 contractbun run --cwd apps/cli test -- src/features/scaffold-state/apply.test.ts src/update/run.test.ts src/cli/check-command.test.ts src/cli/public-output-contract.test.tsMixed outcomes remain typed, independent actions continue, and only successful receipt state persists

Remaining Work

  • None inside IP-318. T1-T8, independent review, Linear reconciliation, draft-PR verification, and the stack dry-run gate are complete. The wiki sync preflight blocker is pre-existing routing drift; direct wiki tests and type generation pass without mutating canonical content.

On this page