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
skillsrepomainbranch; changed changelog paths select the release product;apps/cliowns 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/srctraced by file and line, shadcn docs andpackages/shadcnsource at98a1fe67(v4.21.0), every reader of.devpunks/outside the CLI. - Unknowns marked below: consumer-repo
.gitignorepractice, which runtime features ofsync-subagents.mjsare 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 cause | Count | Issues |
|---|---|---|
| Pack or baseline resolution and authority | 9 | 43, 49, 50, 55, 62, 63, 91, 94, 143 |
| Generated artifact bugs (lint configs, runners, catalogs) | 8 | 29, 32, 38, 51, 56, 207, 224, 232 |
| Generator overwrote repo-tailored files | 7 | 14, 27, 52, 53, 61, 69, 71 |
| Recorded hash or manifest fingerprint drift | 6 | 65, 97, 104, 105, 179, 203 |
| Temporary update candidate or staging failures | 6 | 194, 197, 199, 215, 219, 220 |
| Exit code, JSON, reporting contract | 3 | 39, 40, 41 |
| Non-portable persisted state (absolute paths, ignored) | 2 | 204, 231 |
| Manifest schema skew between writer and reader | 2 | 31, 37 |
| Other | 4 | 11, 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
changedwhen 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 KBscaffold-manifest.jsonwith 654managedFilesentries, a 1.5 MB projection receipt, a 296 KB context plan, 6.7 MB ofpre-existing-skills, plusreplaced-scaffoldandreplaced-skillsarchives.- The manifest records
cwdandoutputDirectoryas/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). Onlycontext-plan.jsonand 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.ymlfile therefore reads aslocal-edited. - Two writers race on the manifest: the CLI, and the shipped
sync-subagents.mjswhich rewrites the receipt hash and projection status out of process (data/scripts/sync-subagents.mjsaround lines 1559-1590). managedStructuredEntriesis computed with key-order-sensitiveJSON.stringifyat 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 writesAGENT-SYSTEM-PROMPT.mdafter 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-393derives the list of protected paths frommanagedFiles[].path. A missing or invalid manifest becomesoperational_failure, which Codex turns intodecision: "block"on every edit (:1158-1165). This is issue #231..agents/scripts/sync-subagents.mjs:24-26reads 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 checkisrunUpdatewithcheck: true(platform/feature-application-operations.ts:262). It forces baselinestablewith 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 inapps/cli/srcand nowhere in the installed@punks/cli@5.2.1dist 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 onlyformat-edited-file.mjs.
Size of the machinery
Slice of apps/cli/src (non-test) | Lines | Share |
|---|---|---|
| Update, scaffold, state, candidate, baseline machinery | 28,857 | 45% |
| Runtime scripts, hooks, lint rules shipped to consumers | 9,338 | 14% |
| Content catalogs (packs, skills, lint assets, subagents) | 5,692 | 9% |
| Everything else | 20,776 | 32% |
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-230spawns oxlint withmaxBuffer: 10 * 1024 * 1024. Overflow surfaces as exit 2kind: "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-36runssh -cwith the same 10 MiB cap; itsgit diff --cachedat:59-62uses 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 asHI_STAGED_FILES(:235-238). Both count towardARG_MAX, hence E2BIG. - The uncommitted worktree changes touch the runner's
PATHhandling 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.jsonlists items;shadcn buildemitspublic/r/<name>.jsonper item. Any HTTPS host works. Private registries useheadersandparamswith${ENV}expansion. - Items are typed file bundles.
registry-item.jsoncarriesname,type,files[] {path, type, target, content},dependencies,devDependencies,registryDependencies,docs,envVars,meta. Types includeregistry:file,registry:item,registry:lib,registry:hook,registry:page,registry:style,registry:base. - Universal items. A
targetstarting with~/lands anywhere under the project root (utils/updaters/update-files.ts:386-507). Files typedregistry:fileorregistry:itemskip every transformer and are written verbatim (update-files.ts:166-172). Such items need only aregistriesmap, not a fullcomponents.json(registry/utils.ts isUniversalRegistryItem). - Consumer state is intent only.
components.jsonstores style, aliases, and theregistriesmap. No manifest, no lockfile, no hashes, no provenance.shadcn infoinfers installed items by scanning disk. - Update is re-add. There is no
updatecommand. Existing files are content-compared after transforms: identical isskipped, different prompts (default No) unless--overwrite.--yesdoes not imply--overwrite. The programmaticaddRegistryItemsskips existing files and never prompts. - Diff is a dry run.
shadcn diffis deprecated and shadcn-index only;add <item> --diffprints the diff against the registry for any registry and writes nothing. - Dependency resolution walks
registryDependenciesrecursively 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 mcpexposes search, view, andget_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@versionfor npm,#reffor GitHub,params.versionfor 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 from | Tier |
|---|---|---|
Skills, 468 files under .agents/skills/<id>/ | Verbatim copy of apps/cli/skills/** via data/catalog/skills.ts | 1 |
| Project-local skills, 4 files | Authored in the repository, producer: project-local-skill | 3 |
Hooks, 2 real files in .agents/hooks/ | Verbatim data/hooks/*.mjs | 1 |
| 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>.md | Verbatim per detected technology (content/source-guides.ts:23,208) | 1 |
Source guide index opensrc/README.md | Rendered 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 workspace | 1 |
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.md | Template data/shared-agents.md rendered with pack prompt details | 2 |
Prompt specs, 17 files .devpunks/specs/prompts/** | Rendered skeletons per workspace × packs; authority: repository | 2 → 3 |
Workspace and docs AGENTS.md files | Authored by the agent from the prompt specs; CLAUDE.md symlink mirrors | 3 |
Subagent spec .devpunks/specs/subagents/manifest-spec.json | Rendered from packs × workspaces (scaffold/output.ts:2418) | 2 |
Subagent manifest .agents/subagents/manifest.mjs + prompt | Authored by the agent from the spec | 3 |
| Per-harness agents, 88 files (22 × Claude, Codex, Cursor, OpenCode) | Projected from manifest.mjs by sync-subagents.mjs; producer: harness-projection | 2 |
Harness entry files (.codex/AGENTS.md, .opencode/AGENTS.md, .claude/skills, .opencode/plugins/*) | Symlinks to .agents/* | 1s |
.codex/config.toml | Baseline 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 owns | 2m |
| DevDependencies, 105 pins across 13 workspaces | Lint asset requiredDevDependencies merged into each package.json | 2m |
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 afterwards | 3 |
Tools (agent-browser, gh, opensrc, portless, skills, …) | Global CLIs ensured by data/catalog/tools.ts; not files | tools |
| Handoff, system prompt, context plan, projection receipt, manifest | Derived bookkeeping | 0 |
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 deaddrift-detectedcode, 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
.devpunksblocks all edits (#231). - Control.
hi updatestages 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.mdis advisory and tells the agent to runhi checkbefore 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.jsonplus 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 whoseregistryDependencieslist their members. Files useregistry:filewith~/targets so they are written verbatim. - Evidence.
apps/cli/skills/**anddata/{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.mdto the baseline product andRELEASE_RECOVERY.jsonto 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.jsonkeeps intent only: registries map, selected packs, lint scopes, backlog provider, required tools, and oneinstalledregistry 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 absolutecwdin 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 updateresolves the selected packs against the latest registry version and writes files.hi diffcompares, 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
--overwriteor 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 --diffalready 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.mdprompts,sync-subagents.mjs, wikisync-content.mjs, Codexconfig.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:pageitems 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/agentsetc.) 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; givegit diff --cachedan 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 diffin summary mode: it fetches the smallregistry.jsoncatalog 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-detectedbranch; five failed findings this session reported as "no drift confirmed"; the forced stable refetch atrepository-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, andworkspaceDependenciesfor per-workspace devDependency pins. Tier 2 items carrytemplatefiles and declare theirinputs(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.envfiles, and installs dependencies at onecwd. - Consequence.
npx shadcn add @hi/<item>still installs the tier 1 files of any item and ignoresmeta, so shadcn stays a usable fallback and theshadcn mcpserver stays usable for discovery. Onlyhiapplies 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 expectcomponents.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 addandshadcn mcpwork 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.devpunksitself. 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:229and the argument expansion are unchanged; the changes touchPATHresolution 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
hiCLI produces the per-harness subagent files itself and the shippedsync-subagents.mjsis 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-dbhas a byte-identical body across.claude,.cursor,.opencode(YAML frontmatter with small field differences) and.codex(TOMLdeveloper_instructions). (Resolves question 4.) higets a small installer of its own that reads the shadcn registry format; item JSON stays byte-compatible soshadcn addandshadcn mcpremain 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
- Is the registry the baseline product for release classification, or a new product with its own changelog path?
- 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?
- Does comparison normalize whitespace and formatter output for text files?
- Do per-harness agent projections become installer-rendered items or stay a shipped script the agent runs?
- Depend on the shadcn package or reimplement a small installer against the
same schema with the
metaextensions from C8? - Does session start run the full local-versus-installed comparison or only the version comparison?
- Should the anti-slop plugin stay vendored into every workspace (26 files here) or be installed once at the root and referenced?
- Fix #232 now on the current runner (C6) before the rewrite starts?
Side findings
- Root
package.jsonnow pointslintat.agents/scripts/managed-lint-runner.mjs, which does not exist in this checkout, sobun run lintis broken by the uncommitted changes. .tmp/landing-cli-proof/holds tracked scratch harness mirrors;.gitignorecovers onlytmp.- The
mainbranch does not contain the #231 fix; it lives onteam/stefan/codex-edit-hooksatffee129a.