Harness Intelligence Wiki
Research

Scaffold and Update Rewrite on a Registry Model

Scaffold and Update Rewrite on a Registry Model

Brainstorm run on 2026-09-27 with four readonly research lanes. The user asked for a new scaffold/update architecture modelled on the shadcn registry CLI, and to tackle issue #232 alongside it. This report records the evidence, the candidate architecture, and the decisions that remain the user's. Nothing here is an accepted requirement.

Boundary

  • System: hi scaffold, hi update, hi check, the persisted state under .devpunks/, the generated .agents/** artifacts and their harness mirrors, and the published baseline that feeds them.
  • Operator: the coding agent (session-start hook, edit hooks, post-command flows) and the human who runs the commands and files reports.
  • Accepted constraints (from repo guidance): shared skills source of truth stays the skills repo main branch; changed changelog paths select the release product; apps/cli owns the executable and generated assets.
  • Constraints added by the user during the brainstorm: the registry must serve the full harness context (skill packs, lint packs, prompt packs, subagents, hooks, scripts, source guides, wiki starter, commit gate), and it must still support update, diff, and check, including drift detection.
  • Evidence: all 47 CLI-flow issues plus linked PRs, apps/cli/src traced by file and line, shadcn docs and packages/shadcn source at 98a1fe67 (v4.21.0), every reader of .devpunks/ outside the CLI.
  • Unknowns marked below: consumer-repo .gitignore practice, which runtime features of sync-subagents.mjs are still relied on, release classification rules for a registry artifact.

Facts

The issue history is dominated by derived state and the update candidate

Classification of 47 issues (numbers 11 to 232), each read in full with comments and closing PRs.

Root causeCountIssues
Pack or baseline resolution and authority943, 49, 50, 55, 62, 63, 91, 94, 143
Generated artifact bugs (lint configs, runners, catalogs)829, 32, 38, 51, 56, 207, 224, 232
Generator overwrote repo-tailored files714, 27, 52, 53, 61, 69, 71
Recorded hash or manifest fingerprint drift665, 97, 104, 105, 179, 203
Temporary update candidate or staging failures6194, 197, 199, 215, 219, 220
Exit code, JSON, reporting contract339, 40, 41
Non-portable persisted state (absolute paths, ignored)2204, 231
Manifest schema skew between writer and reader231, 37
Other411, 45, 58, 64
  • 15 issues stem from the CLI persisting derived state it later compares (hashes, receipts, plans, recorded packs). 19 stem from generation logic. 13 stem from candidate mechanics, control-plane authority, or reporting.
  • Hash drift was "fixed" four times (PR #66, PRs #112/#117, PR #195 era, PR #208) and recurred after each fix. Issue #203 states the class plainly: pristine scaffold bytes report changed when scaffold output and the later desired-state compilation do not converge.
  • Candidate staging was fixed four times in 14 days (PRs #195, #198, #216, #223); each fix exposed the next missing candidate input (receipt, binaries, Unix sockets, symlink targets and lockfiles).
  • Issue #94's own body says it "recurs the authority failure class repaired in issue #91".
  • Explicit manual workarounds appear in #104 (archive 29 skill files then 14 scaffold files by hand), #231 (backport the hook's ownership loader), #219 (hold and restore mirrors, republish receipts), #203 ("no supported command to accept arbitrary bytes for an exact-byte artifact"), #43 (run the repo-local CLI instead of the global one).

Persisted state today

  • .devpunks/ in this repository is 10 MB and tracked in git: a 444 KB scaffold-manifest.json with 654 managedFiles entries, a 1.5 MB projection receipt, a 296 KB context plan, 6.7 MB of pre-existing-skills, plus replaced-scaffold and replaced-skills archives.
  • The manifest records cwd and outputDirectory as /home/stefan/repos/harness-intelligence, an absolute path from a different machine, in a tracked file.
  • Almost every managed-file hash is a raw sha256 of bytes (integrations/scaffold-content-hasher.ts:5-15). Only context-plan.json and the projection receipt get canonical JSON hashing (scaffold/json.ts:17-52). Reconcile uses raw hashes only (features/scaffold-state/reconcile.ts:645-672). Any formatter pass over a managed .md, .ts, or .yml file therefore reads as local-edited.
  • Two writers race on the manifest: the CLI, and the shipped sync-subagents.mjs which rewrites the receipt hash and projection status out of process (data/scripts/sync-subagents.mjs around lines 1559-1590).
  • managedStructuredEntries is computed with key-order-sensitive JSON.stringify at manifest time (scaffold/output.ts:4851-4874) but compared with sorted stringification at update time (update/run.ts:557-570).
  • Scaffold writes settings.json, runs local quality setup, cleans .gitignore, and writes AGENT-SYSTEM-PROMPT.md after the manifest is written (platform/scoped-scaffold-operation.ts:232-300).

Who actually reads that state at runtime

Outside the CLI's own scaffold/update/check code, only two runtime consumers read .devpunks/:

  • .agents/hooks/format-edited-file.mjs:40,374-393 derives the list of protected paths from managedFiles[].path. A missing or invalid manifest becomes operational_failure, which Codex turns into decision: "block" on every edit (:1158-1165). This is issue #231.
  • .agents/scripts/sync-subagents.mjs:24-26 reads the manifest, the receipt, and the context plan when it re-projects .claude/agents, .codex/agents, .cursor/agents, and .opencode/agents.

Everything else is advisory or CLI self-verification: AGENT-HANDOFF.md, AGENT-SYSTEM-PROMPT.md, specs/prompts/**, replaced-*, pre-existing-skills. settings.json is read by the managed lint runner for lint.scopes and by the backlog and asset skills for provider routing. Nothing under .github/workflows reads .devpunks/.

The session-start check is network-bound and its drift branch is dead

  • hi check is runUpdate with check: true (platform/feature-application-operations.ts:262). It forces baseline stable with refresh (features/repository-check/application.ts:592-597), so every agent session start fetches the control plane and hashes 654 files.
  • The hook only prints its "divergence detected" remediation when an issue code equals drift-detected (.agents/hooks/scaffold-update-check.mjs:140-151). That code appears nowhere in apps/cli/src and nowhere in the installed @punks/cli@5.2.1 dist except the hook file itself. Real drift is reported as "check unavailable, no scaffold drift confirmed". The check that opened this session printed exactly that while reporting five failed findings.
  • OpenCode never runs the check: .opencode/plugins/ links only format-edited-file.mjs.

Size of the machinery

Slice of apps/cli/src (non-test)LinesShare
Update, scaffold, state, candidate, baseline machinery28,85745%
Runtime scripts, hooks, lint rules shipped to consumers9,33814%
Content catalogs (packs, skills, lint assets, subagents)5,6929%
Everything else20,77632%

update/run.ts alone is 7,425 lines with a seven-phase pipeline (baseline, planning, tools, preparation, lint, apply, cleanup), around 30 validation-error sites in planning, 7 lint-preview failure kinds across 5 phases, and a non-atomic apply where direct copies land before the receipt is persisted (update/run.ts:6473-6550, then features/scaffold-state/apply.ts:294-428).

Issue #232 mechanics

  • data/scripts/managed-lint-runner.mjs:225-230 spawns oxlint with maxBuffer: 10 * 1024 * 1024. Overflow surfaces as exit 2 kind: "process". The same function is the update lint-preview executor (runtime/scripts.ts:22,1298). The generic preview runner already allows 256 MiB (runtime/scripts.ts:117-125) but lint bypasses it.
  • data/scripts/commit-gate-runner.mjs:32-36 runs sh -c with the same 10 MiB cap; its git diff --cached at :59-62 uses Node's 1 MiB default.
  • When any route input changes (settings.json, selection.json, lockfiles, configs), the complete owner file set is shell-quoted into one command string (commit-gate-runner.mjs:163-208) and also exported as HI_STAGED_FILES (:235-238). Both count toward ARG_MAX, hence E2BIG.
  • The uncommitted worktree changes touch the runner's PATH handling and the policy planner, not the buffer or the argument expansion. #232 is unaddressed.

What shadcn's model is, precisely

Sources: ui.shadcn.com registry docs and packages/shadcn/src at 98a1fe67.

  • Server side is static JSON. registry.json lists items; shadcn build emits public/r/<name>.json per item. Any HTTPS host works. Private registries use headers and params with ${ENV} expansion.
  • Items are typed file bundles. registry-item.json carries name, type, files[] {path, type, target, content}, dependencies, devDependencies, registryDependencies, docs, envVars, meta. Types include registry:file, registry:item, registry:lib, registry:hook, registry:page, registry:style, registry:base.
  • Universal items. A target starting with ~/ lands anywhere under the project root (utils/updaters/update-files.ts:386-507). Files typed registry:file or registry:item skip every transformer and are written verbatim (update-files.ts:166-172). Such items need only a registries map, not a full components.json (registry/utils.ts isUniversalRegistryItem).
  • Consumer state is intent only. components.json stores style, aliases, and the registries map. No manifest, no lockfile, no hashes, no provenance. shadcn info infers installed items by scanning disk.
  • Update is re-add. There is no update command. Existing files are content-compared after transforms: identical is skipped, different prompts (default No) unless --overwrite. --yes does not imply --overwrite. The programmatic addRegistryItems skips existing files and never prompts.
  • Diff is a dry run. shadcn diff is deprecated and shadcn-index only; add <item> --diff prints the diff against the registry for any registry and writes nothing.
  • Dependency resolution walks registryDependencies recursively with a visited set, topologically sorts, and lets the last writer win on duplicate targets (registry/resolver.ts:148-400).
  • MCP is read-only. shadcn mcp exposes search, view, and get_add_command_for_items, which returns the shell command rather than installing.
  • Not supported: three-way merge, rename or delete tracking, removal, symlinks as file content, post-install scripts, versions beyond registry-side conventions (name@version for npm, #ref for GitHub, params.version for namespaces).

Full harness inventory and how each kind is produced

The current manifest in this repository records 654 managed files plus 31 structured JSON entries and 105 per-workspace devDependency pins. Grouped by how the bytes come to exist:

Kind (manifest kind, count)Produced fromTier
Skills, 468 files under .agents/skills/<id>/Verbatim copy of apps/cli/skills/** via data/catalog/skills.ts1
Project-local skills, 4 filesAuthored in the repository, producer: project-local-skill3
Hooks, 2 real files in .agents/hooks/Verbatim data/hooks/*.mjs1
Hook mirrors, 6 symlinks in `.claude.codex.cursor/hooks/`
Scripts, 8 files in .agents/scripts/Verbatim data/scripts/** (sync-subagents, harness-projection)1
Source guides, 5 files opensrc/<tech>.mdVerbatim per detected technology (content/source-guides.ts:23,208)1
Source guide index opensrc/README.mdRendered from the selected guides (content/source-guides.ts:157)2
Anti-slop lint plugin, 26 files (LICENSE + index.mjs × 13 workspaces)Verbatim data/lint/anti-slop, copied into every workspace1
oxlint.config.ts, 13 files (root + 12 workspaces)Rendered from selected lint assets × workspace detection (data/catalog/lint.ts, scaffold/managed-lint-generation.ts)2
Lint spec, 3 files (assets.json, selection.json, README.md)assets.json is a catalog copy; selection.json is intent (packs → asset ids)2 / intent
Shared prompt .agents/AGENTS.mdTemplate data/shared-agents.md rendered with pack prompt details2
Prompt specs, 17 files .devpunks/specs/prompts/**Rendered skeletons per workspace × packs; authority: repository2 → 3
Workspace and docs AGENTS.md filesAuthored by the agent from the prompt specs; CLAUDE.md symlink mirrors3
Subagent spec .devpunks/specs/subagents/manifest-spec.jsonRendered from packs × workspaces (scaffold/output.ts:2418)2
Subagent manifest .agents/subagents/manifest.mjs + promptAuthored by the agent from the spec3
Per-harness agents, 88 files (22 × Claude, Codex, Cursor, OpenCode)Projected from manifest.mjs by sync-subagents.mjs; producer: harness-projection2
Harness entry files (.codex/AGENTS.md, .opencode/AGENTS.md, .claude/skills, .opencode/plugins/*)Symlinks to .agents/*1s
.codex/config.tomlBaseline bytes, but users tailor it (#71)3
Structured entries, 31 (.claude/settings.json hooks, .cursor/hooks.json, package.json lint/format:check/prepare in 13 workspaces)JSON field merges into files the repository owns2m
DevDependencies, 105 pins across 13 workspacesLint asset requiredDevDependencies merged into each package.json2m
Commit gate (lefthook.yml merge, .devpunks/commit-gate-contracts.json, runner)Rendered from lint scopes and owners (scaffold/output.ts:398,3451)2 / 2m
Wiki starter under apps/wiki/Rendered once (content/wiki.ts, 1,383 lines); project-owned afterwards3
Tools (agent-browser, gh, opensrc, portless, skills, …)Global CLIs ensured by data/catalog/tools.ts; not filestools
Handoff, system prompt, context plan, projection receipt, manifestDerived bookkeeping0

Tier meanings: 1 verbatim files (about 85% of managed files); 1s symlinks; 2 deterministic renders from registry content × intent × repository shape; 2m merges into repository-owned JSON or YAML; 3 scaffold-once, then authored by the agent or human; 0 exists only so the CLI can check itself.

Against the shadcn item model: tier 1 maps directly to registry:file with ~/ targets. Tier 1s, 2m, and per-workspace devDependencies have no upstream equivalent (shadcn writes content files only, merges only .env, and installs npm dependencies at one cwd). Tier 2 needs the generator and its inputs to travel with the registry. Tier 3 maps to shadcn's skip-existing behaviour.

Operating the system from the agent's seat

  • Intake. Session start runs a network-bound hi check. When it fails for any reason other than the dead drift-detected code, the agent is told "no scaffold drift confirmed" and continues on possibly stale scaffolding.
  • State. The agent cannot tell which of 654 recorded hashes matter. The edit hook needs the manifest on every edit, so a fresh worktree in a repo that ignores .devpunks blocks all edits (#231).
  • Control. hi update stages a full temporary copy of the repository, installs dependencies, runs lint preview, then applies. Five of the six candidate issues are environmental facts the candidate could not reproduce (missing binaries, Bun store links, sockets, symlink targets, nested lockfiles).
  • Feedback. Drift has four per-file states plus pack, baseline, pin, and obligation drift, each with its own code. Formatting a managed file is indistinguishable from editing it.
  • Recovery. There is no command to accept the current bytes of a managed file. Recovery is manual archiving into replaced-* directories (#104, #203) or CLI source edits (#232).
  • Handoff. AGENT-HANDOFF.md is advisory and tells the agent to run hi check before trusting it.

Inferences

  • The recurrence pattern is structural, not a run of unlucky bugs. Every hash-drift fix tightened one generator or hasher while leaving the invariant "recorded bytes must equal regenerated bytes" in place. That invariant cannot hold across formatters, second writers, and post-manifest writes.
  • The candidate exists to guarantee "update never leaves the repo failing lint". The issue history shows the candidate itself is the most common reason update fails, and the guarantee is already violated whenever the candidate environment diverges from the live one.
  • Only one piece of derived state is load-bearing at runtime: the set of managed paths the edit hook protects. Everything else that is persisted exists so the CLI can verify its own previous output.
  • The shadcn model works because it removed the consumer-side ledger and moved authority entirely to the registry. Its cost is that it cannot tell a local edit from an upstream change without a dry-run diff. For this project that distinction is exactly what has been unreliable for four months, so the cost is lower than it looks.

Candidate architecture

Each candidate is stated with evidence, consequence, and the tradeoff still open. None is accepted.

C1. Publish the baseline as a static registry

  • Change. Emit registry.json plus one JSON per item from the existing baseline build (apps/cli/scripts/publish-baseline.mjs). Items: one per skill, hook, runtime script, lint asset, subagent template, and prompt-spec scaffold. Packs become items whose registryDependencies list their members. Files use registry:file with ~/ targets so they are written verbatim.
  • Evidence. apps/cli/skills/** and data/{scripts,hooks,lint} already are flat file trees with stable ids; the pack catalog already maps pack to skills, lint assets, hooks, and prompt surfaces (data/catalog/packs.ts:13-25).
  • Consequence. Removes the tarball, control-plane metadata, digest cross-checks, and the verified-baseline cache (baseline/resolve.ts, 9 authority issues). The wiki, docs, and hooks can point at one URL.
  • Open tradeoff. Release classification rules in repo guidance bind BASELINE_CHANGELOG.md to the baseline product and RELEASE_RECOVERY.json to provider readback. Whether a static registry is "the baseline product" or a new product needs a human decision.

C2. The registry is the ledger; the repository keeps intent and one version

  • Change. Publish every registry version under its own path (for example /r/2026.09.15/<item>.json), so old versions stay fetchable. settings.json keeps intent only: registries map, selected packs, lint scopes, backlog provider, required tools, and one installed registry version string. A second tracked file lists managed paths, no hashes, derived from the installed items' files[].target.
  • Evidence. The edit hook needs paths only (format-edited-file.mjs:374-393). No runtime reader needs a hash. Both "what was installed" and "what is current" become registry fetches keyed by version, so nothing derived is stored in the repository. The absolute cwd in the tracked manifest and #204 show what persisting machine facts costs.
  • Consequence. Deletes the root cause of six hash-drift and two portability issues. .devpunks/ shrinks from 10 MB to kilobytes. Fresh worktrees work because the path list is small enough to always track. A version string is a stable identity; a hash of formatted bytes is not.
  • Open tradeoff. Tier 2 renders depend on repository shape (workspace list, detected technologies). To tell "you edited this config" from "a workspace was added", the installer must re-render at the installed version with the inputs used at install time. That needs a small recorded input set (about a dozen workspace paths and pack ids) in settings.json. It is derived state, but tiny, human-readable, and compared semantically rather than by bytes. The user decides whether that is acceptable.

C3. Update is add with overwrite; diff and check are three-way against the registry

  • Change. hi update resolves the selected packs against the latest registry version and writes files. hi diff compares, per managed path, three things: local bytes, the item at the installed version, and the item at the latest version. Tier 1 files compare directly; tier 2 files compare after re-rendering at each version with the recorded inputs; tier 3 files are never byte-compared and only report "template changed since you authored".
  • Drift classes that fall out of the comparison.
    • local equals installed, installed differs from latest: upstream update available.
    • local differs from installed, installed equals latest: local edit.
    • both differ: conflict, shown as a diff, resolved by --overwrite or by keeping the local file.
    • tier 2 re-render with current inputs differs from re-render with recorded inputs: shape drift, meaning the repository changed and regeneration is due.
    • a path in the installed item set that is absent locally: missing.
    • a path in the installed set but not in the latest set: stale, removable on update.
  • Evidence. shadcn's add --diff already performs the local-versus-latest half (utils/dry-run.ts:36-44, update-files.ts:144-260); the missing half is fetching the installed version, which versioned static hosting provides. Issue #203 asked for a way to accept current bytes; here acceptance is keeping the file, and the next update reports a conflict only if upstream also changed.
  • Consequence. The same four drift states the CLI reports today, but with no recorded hashes, no reconcile lane, no pending-publication recovery, and no candidate. Formatting noise can be removed by comparing normalized content for text files, a policy choice rather than a hashing accident.
  • Open tradeoff. Whether comparison normalizes whitespace or formatter output. Normalizing hides real edits that are whitespace-only; not normalizing reintroduces issue #65 as a visible but harmless "local edit".

C4. Scaffold-once files are project-owned after first write

  • Change. AGENTS.md prompts, sync-subagents.mjs, wiki sync-content.mjs, Codex config.toml, oxlint.config.ts, and other repo-tailored outputs are written only when absent (or with an explicit --overwrite <path>). They are never compared afterwards.
  • Evidence. Seven issues (14, 27, 52, 53, 61, 69, 71) are the generator replacing files the operator had tailored. shadcn's registry:page items and its "skip existing" programmatic path model this.
  • Consequence. The generator stops fighting the repository. Upstream improvements to those templates arrive through a visible hi diff, not a silent rewrite.
  • Open tradeoff. Whether the per-harness agent projections (.claude/agents etc.) stay generated by a shipped 3,265-line script or become registry items per harness. The script is a second manifest writer today; under C2 it would have nothing to write. Unknown which of its behaviours consumers still rely on.

C5. Drop the temporary candidate and lint preview

  • Change. Apply directly, then run the repo's own lint through the managed runner and report the result. No punks-update-candidate-*, no candidate install, no validation cache, no Turbo remote cache for update.
  • Evidence. Six candidate issues; the validation cache and its remote store exist only to make the candidate affordable (integrations/update-cache-*.ts, features/scaffold-update/validation-cache).
  • Consequence. Removes the largest single failure surface and the slowest phase. Update becomes idempotent and re-runnable.
  • Open tradeoff. Loses the promise that update never leaves lint red. A red lint after update is now a visible diff plus a lint report, which is what the operator ends up with anyway when the candidate fails.

C6. Fix #232 independently and immediately

  • Change. Stream oxlint JSON to a temp file or raise the cap to match the 256 MiB preview runner; pass file sets through stdin or a response file and drop HI_STAGED_FILES; give git diff --cached an explicit buffer.
  • Evidence. managed-lint-runner.mjs:229, commit-gate-runner.mjs:35,59-62, 186-238; the issue reports 13.5 MB of oxlint output and a reproduced fix at 100 MiB.
  • Consequence. Unblocks the reporting repository now. The runner and gate scripts are registry items under C1, so the fix carries over.
  • Open tradeoff. Whether the commit gate's "expand the whole owner set on config change" rule survives at all under C3, where a config change is an ordinary diff.

C7. Make hi check cheap and honest

  • Change. Check is hi diff in summary mode: it fetches the small registry.json catalog once, compares the installed version to the latest, verifies required tools, and reports counts per drift class from C3. Full content fetches happen only for items whose version changed, and the per version item JSON is cacheable forever because versions are immutable.
  • Evidence. The dead drift-detected branch; five failed findings this session reported as "no drift confirmed"; the forced stable refetch at repository-check/application.ts:592-597.
  • Consequence. Session start is fast, offline-tolerant (installed version items are already cached), and its message is true.
  • Open tradeoff. Whether session start should run the full local-versus installed comparison (detects local edits, costs a directory walk) or only the version comparison (detects upstream updates only).

C8. Installer extensions the schema does not cover

  • Change. Keep registry item JSON byte-compatible with shadcn and put the three unsupported needs under meta: symlinks[] {path, target} for the harness mirrors, merge[] {file, path, value} for structured JSON and YAML entries, and workspaceDependencies for per-workspace devDependency pins. Tier 2 items carry template files and declare their inputs (packs, workspaces, technologies) so the installer can re-render them.
  • Evidence. 10 managed symlinks, 31 structured entries, 105 devDependency pins in this repository alone; shadcn writes content files only (update-files.ts:386-507), merges only .env files, and installs dependencies at one cwd.
  • Consequence. npx shadcn add @hi/<item> still installs the tier 1 files of any item and ignores meta, so shadcn stays a usable fallback and the shadcn mcp server stays usable for discovery. Only hi applies the extensions.
  • Open tradeoff. Whether per-harness agent projections (88 files) become tier 2 items rendered by the installer, replacing the shipped 3,265-line sync-subagents.mjs, or whether that script stays a tier 1 item that the agent runs. The former removes a second manifest writer; the latter keeps the current authoring flow untouched.

Reuse versus reimplement

  • Option A: depend on shadcn's programmatic API (shadcn/registry, addRegistryItems). Gains the resolver, fetcher, env expansion, and dry-run for free. Cost: coupling to a React-centric CLI whose non-universal paths expect components.json, and whose overwrite prompt ignores --yes.
  • Option B: adopt the schema and hosting model, keep a small in-house installer. The resolver is under 400 lines upstream; the installer for verbatim ~/ files is smaller. Cost: one more thing to maintain; gain: no prompt or transformer surprises, and Effect-native errors.
  • Recommendation for discussion: Option B for the installer, with the registry JSON kept byte-compatible so npx shadcn add and shadcn mcp work as a fallback and as agent discovery.

Conflicts resolved during synthesis

  • Issue #231 says the manifest is gitignored; here it is tracked. Both are true: the CLI never writes ignore entries (scaffold/gitignore.ts:10,24-27), and the reporting repository ignored .devpunks itself. Consumer practice is therefore an unknown that C2 must not depend on.
  • One lane inferred the worktree changes fix #232. The buffer at managed-lint-runner.mjs:229 and the argument expansion are unchanged; the changes touch PATH resolution and policy planning only.
  • One lane could not tell whether the installed CLI still emits drift-detected. It does not: the string exists only in the hook files under the installed dist.

Decisions recorded on 2026-09-27

  • The registry is the new Baseline distributor. Baselines are pushed to the registry instead of GitHub release artifacts, and the registry is the source for drift management. (Resolves question 1.)
  • Issue #232 is fixed inside the new release that ships the registry implementation, not as a separate fix on the current runner. (Resolves question 8.)
  • Built Managed Artifacts (oxlint configs, prompt specs, subagent spec, shared prompt, commit gate) keep their install-time repository shape (workspace paths, Pack ids) in Project settings, so the Drift check can separate a local edit from a shape change. (Resolves question 2, option A.)
  • The hi CLI produces the per-harness subagent files itself and the shipped sync-subagents.mjs is removed. The prompt body is built once from .agents/subagents/manifest.mjs; each Harness adapter only wraps it in that harness's envelope. Evidence: packages-db has a byte-identical body across .claude, .cursor, .opencode (YAML frontmatter with small field differences) and .codex (TOML developer_instructions). (Resolves question 4.)
  • hi gets a small installer of its own that reads the shadcn registry format; item JSON stays byte-compatible so shadcn add and shadcn mcp remain fallbacks. (Resolves question 5.)
  • Still open: question 6 (session start runs the full local-versus-installed comparison or only the version comparison) and question 7 (anti-slop plugin vendored per workspace or installed once at the root).

Unresolved decisions for the user

  1. Is the registry the baseline product for release classification, or a new product with its own changelog path?
  2. Is a recorded input set for tier 2 renders (workspace paths, pack ids, technologies) acceptable derived state, or must shape drift be inferred another way?
  3. Does comparison normalize whitespace and formatter output for text files?
  4. Do per-harness agent projections become installer-rendered items or stay a shipped script the agent runs?
  5. Depend on the shadcn package or reimplement a small installer against the same schema with the meta extensions from C8?
  6. Does session start run the full local-versus-installed comparison or only the version comparison?
  7. Should the anti-slop plugin stay vendored into every workspace (26 files here) or be installed once at the root and referenced?
  8. Fix #232 now on the current runner (C6) before the rewrite starts?

Side findings

  • Root package.json now points lint at .agents/scripts/managed-lint-runner.mjs, which does not exist in this checkout, so bun run lint is broken by the uncommitted changes.
  • .tmp/landing-cli-proof/ holds tracked scratch harness mirrors; .gitignore covers only tmp.
  • The main branch does not contain the #231 fix; it lives on team/stefan/codex-edit-hooks at ffee129a.

On this page