Plan: onboarding hunkydory, and what TypeScript means for the fleet¶
Prompt¶
Before responding to questions or discussion points in this document, explore this repository thoroughly. Read the relevant files and ground your answers in what they actually say. Do not speculate about the repository when you could read it instead. Flag any uncertainty explicitly rather than guessing.
There is no application code here. The artifacts are the audit
specifications in docs/audits/, the tooling in scripts/ that
measures them, the templates in templates/ that the rest of the
fleet copies, and the workflows that run all of it every morning
against every Shaken Fist repository.
Consult AGENTS.md for the conventions and the invariants that
are not visible in the code, and ARCHITECTURE.md for the shape
of the system. docs/consistency-audits.md is the reference for
what a daily run does, how to add a criterion, how to bring a
repository into scope, and how to test a change before it reaches
the fleet -- read it before changing anything under scripts/ or
docs/audits/. docs/code-review-tracking.md covers the review
tooling, and PUSH-AUDIT.md is the pre-push review runbook that
every plan's final phase runs.
This plan is unusual for this repository in that most of its work
lands somewhere else: in hunkydory, and in the 33fl repository
in the other organisation, which owns the static runner fleet. What
lands here is the scope registration, the npm dependency criteria,
and this document.
Situation¶
shakenfist/hunkydory was created on 2026-09-11. It is a VS Code
extension that keeps the @@ hunk headers in a patch file correct
while the file is edited, written in TypeScript with no runtime
dependencies. It is the fleet's first TypeScript repository, and
nothing in the audit, the templates or the runner images has met the
language before.
Three measurements frame the work.
The audit already runs against it, and mostly passes. Running
the checker by hand against a clone, as
docs/consistency-audits.md instructs before onboarding:
python3 scripts/audit-check.py --repo-path ~/src/shakenfist/hunkydory \
--repo-name hunkydory --github-org shakenfist
reports 51 checks: 6 pass, 9 fail, 36 not applicable. The nine failures are all infrastructure rather than anything about the code:
| Check | Why it fails |
|---|---|
pre-commit-config |
No .pre-commit-config.yaml |
llm-context-lint-ci |
skillsaw runs from neither pre-commit nor CI |
renovate |
No renovate.json, no renovate.yml |
ci-review-automation |
No pr-re-review.yml, no pr-retest.yml, no reviewer action |
export-repo-config |
No export-repo-config.yml |
github-security |
No CodeQL workflow; secret scanning and push protection off |
delete-branch-on-merge |
Not enabled on the repository |
readme-absolute-links |
Three relative links in README.md |
docs-external-links |
One relative link in docs/ leaving docs/ |
The documentation criteria -- llm-tooling, llm-doc-structure,
readme-structure, diagram-format, plan-phase-references --
already pass, as does default-branch-naming: the repository was
created with develop as its default branch.
Nine of the 36 not-applicable results are not-applicable because
the criterion is Python-specific, not because the concern does not
exist. release-process, pin-indirect-dependencies,
dependency-name-normalization, unused-declared-dependency,
undeclared-direct-dependency, renovate-lockstep-groups,
version-file-gitignore and python-version-targeting all key off
pyproject.toml, and pyproject-usage off the presence of Python.
A TypeScript repository with a package.json and a
package-lock.json raises the same questions about pinning and
unused dependencies, and today the audit simply does not ask them.
The runners cannot run npm. No workflow anywhere in the fleet
uses setup-node, npm ci or npm install; the mermaid-lint
template records the reason, that running it "from the upstream
container keeps chromium and a node toolchain off the runners", and
that jsdom "pulls in an undici that needs a newer node than the
runners carry". The static runners boot debian:12
(33fl/static_runner.yml:229), which packages nodejs 18.20.4.
Debian 13 packages nodejs 20.19.2. Both were confirmed against the
archive rather than assumed.
hunkydory itself was verified to build and pass its 20 unit tests on
Debian 12's nodejs 18.20.4 inside a container, and Biome 2.5.13 was
verified to install and run there too. So node 18 is sufficient;
the decision to move to Debian 13 below is taken for other reasons.
Mission and problem statement¶
Bring hunkydory under the consistency audits and the human review
tracking, and in doing so decide what the fleet's conventions mean
for TypeScript: how npm runs in CI, what lints a TypeScript project,
what a pre-commit configuration looks like for it, and how its
dependencies are audited.
The plan deliberately does not try to make TypeScript a first-class citizen of every criterion. It answers the questions hunkydory actually raises, and leaves a second TypeScript repository to generalise from two examples rather than one.
It also does not cover the extension's own functionality, which is complete and tested, or its publication to the VS Code Marketplace beyond the mechanics of a release workflow.
Decisions¶
D1. npm runs on static runners, from Debian packages, on Debian 13¶
Three options were considered: actions/setup-node on the existing
static pool, a pinned node:20 container on an ephemeral VM runner,
and installing Debian's nodejs on the runner image.
setup-node is the wrong shape here. The static runners are not
ephemeral -- private-ci describes them as "a static shared runner
rather than an ephemeral per-job VM" -- so setup-node leaves a
tool cache under _work/_tool/node/ that grows without bound and is
shared by every repository using the pool. It also downloads a node
that Debian already packages. The PATH change itself is harmless and
job-scoped, via GITHUB_PATH; the disk state is the problem.
The container option touches no shared state at all, and matches
what ryll and mermaid-lint already do, but it spends the scarcest
runner pool (l, six workers fleet-wide) on a thirty-second build.
So: nodejs and npm from Debian, installed on the static runner
image. That makes npm a first-class fleet capability rather than a
hunkydory workaround, keeps the runtime on Debian's security
support, and leaves CI as npm ci with nothing to download.
Taken together with the version question, this means moving the
static runners from debian:12 to debian:13. hunkydory runs fine
on Debian 12's node 18.20.4, so this is not forced by hunkydory.
It is taken because node 18 reached upstream end of life in April
2025 and Debian 13's node 20.19.2 both matches what the project is
developed against and buys years rather than months. The conductor
already builds a debian-13 label from a debian:13 base image, so
the image is cached on the cluster, which is the precondition
static_runner.yml documents at its line 39.
The blast radius is the reason this is its own phase: it re-images
every static and claude-code runner in both the shakenfist
and mach33labs organisations.
D2. Biome, not ESLint¶
One devDependency that both lints and formats, against roughly six
for eslint + typescript-eslint + prettier and their configs.
hunkydory has no runtime dependencies and the smaller surface keeps
it that way, and keeps Renovate quiet. Biome 2.5.13 was confirmed to
run on node 18.20.4, so this decision does not depend on D1 landing.
The cost is a smaller rule set and a smaller ecosystem than ESLint, which is the conventional choice for a VS Code extension. If a second TypeScript repository wants ESLint specifically, that is the point to revisit rather than now.
Biome needs a biome.json matching the conventions AGENTS.md
already states -- 100 character lines, single quotes, semicolons --
because its defaults disagree with all three.
D3. Publish to the VS Code Marketplace¶
Rather than deferring releases or attaching a .vsix to a GitHub
release, hunkydory publishes with vsce publish. The extension is
meant to be installed by people who are not us, and an extension
nobody can install from inside VS Code is one nobody installs.
This is the one decision with a prerequisite outside any repository:
an Azure DevOps publisher account for the shakenfist publisher id
already named in package.json, and a VSCE_PAT repository secret.
Until both exist the release phase cannot be completed, which is why
it is sequenced last and marked Blocked.
D4. Write npm dependency criteria now¶
The three Python dependency criteria -- pin-indirect-dependencies,
unused-declared-dependency, undeclared-direct-dependency -- get
npm equivalents in this plan rather than being recorded as not
applicable.
The weaker option was available and was rejected: package-lock.json
does pin the full transitive tree and npm ci does enforce it, so
pin-indirect-dependencies is arguably satisfied by construction.
But that reasoning covers one of the three. Nothing checks that a
declared dependency is actually imported, or that an import is not
resting on a transitive pin, and those are the two that catch real
drift. Writing all three keeps the npm story symmetric with the
Python one instead of leaving two thirds of it unmeasured.
The Python criteria that stay not applicable need no override to say
so. release-process, pin-indirect-dependencies,
dependency-name-normalization, unused-declared-dependency,
undeclared-direct-dependency, renovate-lockstep-groups,
version-file-gitignore and python-version-targeting each begin by
skipping when has_pyproject_toml is false, and pyproject-usage
falls through to skip('No Python code') when git ls-files -- '*.py'
comes back empty. That is already why they are among the 36
not-applicable results in the Situation section.
Nor is there a facility to say it. detect_repo_properties()
(scripts/audit/repo.py:108-133) reads exactly six override keys --
is_private, is_docs_only, not_python, default_branch_exception,
only_checks and doc_content_excludes -- and discards anything else
silently, so a per-criterion not-applicable-with-reason entry would be
invented shape that no test would catch and no check would read. If
such a facility is ever wanted it is a change to
detect_repo_properties() with its own phase and its own test, not a
line in this plan's phase 1.
D5. 33fl is not touched by this plan's author¶
Another session is editing 33fl concurrently. The runner phase
below specifies the change and its risks but is not to be executed
until that work has landed and the operator says so. Nothing else in
the plan writes to that repository.
Execution¶
| Phase | Status | Merged |
|---|---|---|
| 1. Register hunkydory in the audit scope | Not started | |
| 2. hunkydory adopts the local tooling | Not started | |
| 3. npm dependency criteria | Not started | |
| 4. Static runners gain node | Blocked | |
| 5. hunkydory CI and the fleet workflows | Not started | |
| 6. Human review onboarding | Not started | |
| 7. Marketplace release | Blocked | |
| 8. Push audit | Not started |
Phase 4 is blocked on D5: 33fl has another session working in it.
Phase 5 depends on phase 4, because a CI workflow that runs npm ci
on a runner without npm is a workflow that fails on arrival. Phase 7
is blocked on the publisher account and VSCE_PAT from D3.
Phases 1, 2, 3 and 6 have no such dependency and can proceed in any order. Phase 1 should go first regardless: see its section.
1. Register hunkydory in the audit scope¶
The scope-coverage criterion reconciles the audit lists against
the organisation every morning and reports a repository that appears
in neither the matrix nor the excluded list. hunkydory has existed
since 2026-09-11 and appears in neither, so this criterion is
failing against development now, and will keep failing until this
phase lands. That is why it goes first and alone rather than waiting
for the rest of the plan.
Following docs/consistency-audits.md:
- Add
hunkydoryto the matrix in.github/workflows/consistency-audit.yml. - Add it to the in-scope list in
docs/audits/README.md, and confirm it does not appear in the excluded list.
Those are the three statements audit/scope.py parses -- the matrix,
and the in-scope and excluded lists -- and
AuditScopeIsStatedOnceTest holds them to each other, so they change
together. REPO_OVERRIDES is a separate, fourth statement, read only
against the partial-scope paragraph and only for only_checks; this
phase does not touch it.
No REPO_OVERRIDES entry is needed at all: per D4, the Python
criteria already report not-applicable from the absence of
pyproject.toml, and there is no key that would carry a
per-criterion reason if one were wanted.
The nine failures from the Situation section become nine issues on
hunkydory at the next run. That is the intended behaviour -- they
are the backlog the rest of this plan works through -- but it should
be a deliberate choice rather than a surprise, and the phase says so
here so the next reader knows the issues were expected.
2. hunkydory adopts the local tooling¶
Everything that does not need a runner:
- Biome as a devDependency, with
biome.jsonset to 100 character lines, single quotes and semicolons per D2, and the existing source brought into compliance. tools/check-node.sh, running the build, the tests and Biome, following the pattern ryll uses forscripts/check-rust.sh: one script called by both pre-commit and CI, so the two cannot drift..pre-commit-config.yamlwith that script as alanguage: scriptlocal hook, plus shellcheck, gitleaks and skillsaw from the fleet configuration. skillsaw here closes half ofllm-context-lint-ci: the check requires both a pre-commit entry and a CI route, and its issue title says so -- "LLM context linting in pre-commit and CI". Phase 5'sci.ymlcloses the other half by runningpre-commit run --all-files, which the check accepts viaPRE_COMMIT_RUN_RE. Until phase 5 lands, this criterion stays red.- Fix the three relative links in
README.mdand the one indocs/, closingreadme-absolute-linksanddocs-external-links. - Align
@types/nodewith whatever runtime D1 lands on, and declareengines.node. Today the package declares^20while the runner it is destined for would have had 18, which is how a type definition ends up describing an API the runtime lacks. PUSH-AUDIT.md, copied from this repository, and a line in hunkydory'sAGENTS.mdsaying when to run it. Thepush-auditcriterion measures both the shared blocks and the reference, and reports not-applicable while the file is absent -- so this is the phase that gives phase 8 a runbook to cite rather than a gap to explain.
3. npm dependency criteria¶
Three checks per D4, following the shape
docs/audits/README.md and the template's worked brief describe. A
criterion has six parts here, not four:
- a
Checksubclass withid,specandissue_titleas class attributes; - registration in
scripts/audit/registry.py; - a specification page under
docs/audits/; - a line in
docs/audits/README.md; - a line each in
FROZEN_METADATA,FROZEN_ISSUE_TITLESand the frozen column table inscripts/tests/test_metadata.py, whichtest_audit_metadata_matches_the_frozen_table,test_issue_titles_match_the_frozen_tableandtest_column_names_match_the_frozen_tableassert equality against; - tests in
scripts/tests/test_packaging.py, where the existing dependency criteria are tested, covering pass, fail and not-applicable.test_metadata.pyalso asserts that every check is reachable from some test module.
Adding three checks without touching the frozen tables fails
pre-commit run --all-files, which is the first item on this plan's
own review checklist, so the phase would fail its own gate. This is
the same staleness PLAN-scope-coverage.md:433 corrected in
PUSH-AUDIT.md; the four-part framing survived into an earlier draft
of this plan.
How many repositories this newly fails: one. Every default-branch
tree of all twenty in-scope repositories was listed recursively
through the GitHub API, and package.json appears in exactly one of
them -- hunkydory, at the root. No tree was truncated, so the survey
is complete rather than a sample. The other nineteen, development
included, report not-applicable, and 33fl is outside the audit
fleet. So phase 3 files at most three issues, all on hunkydory, and
they join the backlog phase 1 already accounts for. If a second
repository grows a package.json before this phase runs, re-run the
survey before landing: the answer is what makes this a safe change to
land in one step rather than a scoped rollout.
They apply when package.json is present and report
not_applicable with a reason otherwise.
The false-positive surface is where the work is, and hunkydory
exhibits all of it today. unused-declared-dependency and
undeclared-direct-dependency must exempt:
- Node builtins, in both spellings.
test/corpus.tshasimport fs from 'node:fs'andimport path from 'node:path'; the barefsandpathspellings are equally valid and equally not packages. - Host-provided modules.
src/extension.tshasimport * as vscode from 'vscode', which the extension host injects and which must never be declared as a dependency. A naive undeclared-direct check flags it. - Relative imports.
./diff,../src/recountare not packages. @types/*packages. Consumed bytsc, never imported by name. hunkydory declares@types/nodeand@types/vscodetoday.typescriptitself, and any devDependency invoked fromscriptsinpackage.jsonrather than from animport--@biomejs/biomeafter phase 2, andvsceafter phase 7.
Without those five, the first run files two false issues on
hunkydory: unused-declared-dependency against all three of its
current devDependencies (@types/node, @types/vscode,
typescript), none of which is imported by name, and
undeclared-direct-dependency against vscode and the two
node:-prefixed builtins. Note also that import type and
export ... from are import forms and must be counted as such.
For pin-indirect-dependencies: if it reports on lockfile entries
rather than on package.json, Biome contributes several rather than
one -- @biomejs/biome resolves platform-specific binaries
(@biomejs/cli-linux-x64 and siblings) as optional dependencies.
That is not drift and must not be read as drift.
Note that this repository is inside its own audit matrix: these
checks will run against development too, find no package.json,
and must report not-applicable rather than failing.
4. Static runners gain node¶
Do not execute without the operator's say-so; see D5.
In 33fl/static_runner.yml:
- Line 229, the disk specification in "Create the missing runner
instances", moves from
@debian:12to@debian:13. nodejsandnpmjoin the base package list at approximately line 337.- The comment at line 39, which documents the cached
debian:12image as a precondition, is updated to saydebian:13.
Three things to verify before proposing that change, none of which this plan has checked:
- The playbook installs
yqwithpip --break-system-packages, installs docker through a shareddocker.yml, and installs the claude CLI for the claude flavor. All three need confirming on trixie. - Line 630 sets
--docker-image debian:12for the GitLab docker executor. That is a different thing from the runner's own image and is deliberately left alone here, but somebody should decide whether it moves too. - Whether
python3-venvand the rest of the base list behave the same on trixie.
Landing node on the runners also falsifies half of the mermaid-lint
rationale this plan quotes as evidence in the Situation section, in
four files where it is load-bearing prose:
templates/mermaid-lint/README.md:28,36,
templates/mermaid-lint/mermaid-lint.sh:16,
tools/mermaid-lint.sh:16 and
docs/audits/mermaid-lint-ci.md:108,116. A node toolchain goes onto
the runners deliberately, and node 20.19.2 is no longer "older than
jsdom wants". The chromium half of the argument survives and the
decision does not change, so this is a rewording rather than a
reversal: keep the chromium argument, drop or restate the
node-version one. It is part of this phase rather than future work
because templates/ is copied into ten repositories, and a template
that justifies itself with a fact the fleet has reversed is judged as
the code it becomes.
The rollout is gradual rather than a re-image: line 229 is inside
the loop over missing_runners, so it affects newly created
instances only, and the weekly retire and rebuild cycle replaces the
fleet over about a week. That is a feature -- a bad image shows up
on one runner rather than all of them -- but it means "landed" and
"rolled out" are a week apart, and phase 5 waits for the latter.
5. hunkydory CI and the fleet workflows¶
Once the runners have npm:
ci.ymlrunningtools/check-node.shon[self-hosted, static], withnpm_config_cachepointed into${{ runner.temp }}so the shared~/.npmon a non-ephemeral runner is not written by a repository's build. It also runspre-commit run --all-filesas a job, the way this repository's ownci.ymldoes, so the shellcheck, gitleaks and skillsaw hooks gate a pull request rather than only a clone where somebody ranpre-commit install. That is what closes the CI half ofllm-context-lint-cifrom phase 2.codeql-analysis.yml,export-repo-config.yml,renovate.ymlandrenovate.json,pr-re-review.yml,pr-retest.yml,secret-scan.ymlfrom the fleet templates, closinggithub-security,export-repo-config,renovateandci-review-automation.- Secret scanning, push protection and delete-branch-on-merge
enabled through the GitHub API, closing
github-security's remaining two findings anddelete-branch-on-merge.
CodeQL supports JavaScript and TypeScript directly, so that workflow is a language substitution rather than a new pattern.
This phase activates four criteria that are dormant today.
workflow-permissions, self-hosted-runners, static-runner-tags
and secret-scanning-ci all begin with if not
repo.props['has_workflows_dir']: return self.skip(...), so they are
among hunkydory's 36 not-applicable results purely because it has no
.github/workflows/. Creating that directory makes all four live.
Three are satisfied by what this phase writes: the fleet templates
carry permissions: blocks, and ci.yml names [self-hosted,
static]. The fourth is why secret-scan.yml is in the list above --
secret-scanning-ci requires one of gitleaks, trufflehog or
detect-secrets invoked from a workflow, and reads workflows only,
so the gitleaks hook in .pre-commit-config.yaml would not have
counted on its own. Phase 1 names the failures it creates so they are
a deliberate choice rather than a surprise; this phase creates none,
and that is the point of saying so here.
6. Human review onboarding¶
Deploy the review tracking from docs/code-review-tracking.md so
the operator can work through the code: .vscode/review-scope.toml,
tools/review-tracking.sh, a prune-reviews.yml workflow, and a
generated REVIEWS.md. This turns review-marks-pre-commit,
review-coverage and review-scope-completeness from
not-applicable into real verdicts.
The scope config should cover src/ and test/. The point of this
phase is that the operator has not read code written entirely by an
agent, and the review queue is how that gets fixed.
This phase deliberately opens a review-coverage issue, the way
phase 1 opens nine. review-coverage fails once five or more
in-scope files need review (REVIEW_BACKLOG_THRESHOLD = 5 in
scripts/audit/checks/review.py), and hunkydory's src/ and test/
hold five files between them with none marked, so the criterion goes
red the moment .vscode/review-scope.toml lands and stays red until
the operator has worked the queue. That is the criterion doing its
job, not a regression, and it is why the success criteria below
exempt it.
7. Marketplace release¶
Blocked on the publisher account and VSCE_PAT; see D3.
A release.yml that packages and publishes on a tag, and the
RELEASE-SETUP.md the fleet's release criterion expects.
release.yml does not run on [self-hosted, static]. D1's own
argument is that the static pool is "a static shared runner rather
than an ephemeral per-job VM", shared by every repository in both the
shakenfist and mach33labs organisations, with filesystem and process
state that outlives a job. VSCE_PAT can publish under the
shakenfist publisher id, so putting it in a job's environment on
that pool exposes it to every other repository's jobs on the same
machine -- and npm ci runs dependency lifecycle scripts there, so a
typosquatted transitive package in any repository using the pool runs
code where the token has been. So:
- the publish job runs on an ephemeral VM runner, not the static pool;
VSCE_PATis a GitHub environment secret on a tag-protected environment, not a plain repository secret, so it is not readable from a job on a branch;- build and publish are separate jobs: the build runs
npm ciand produces the.vsixas an artifact with no access to the secret, and the publish job uploads that artifact without running install scripts.
If the static pool is used anyway, this phase says why the exposure is acceptable, here, where the next reader finds it.
Note that
release-process as written measures a Python package and will stay
not applicable; whether it grows a TypeScript arm is future work
rather than part of this plan.
8. Push audit¶
Run PUSH-AUDIT.md over the accumulated diff of every phase against
main, per the shared block. Phases landing in hunkydory and
33fl record <repo> <sha> (#pr) in the Merged column.
Neither hunkydory nor 33fl carries a PUSH-AUDIT.md today, so
as this plan is written there is no runbook in either to cite. The
push-audit criterion reports not-applicable when the file is
absent, which is why it is not among
the nine failures the Situation section enumerates, and 33fl is
outside the audit fleet entirely. The shared block anticipates this:
"A repository with no PUSH-AUDIT.md still carries the phase, and
the phase says that the runbook does not exist yet and what was done
instead."
What is done instead: phase 2 deploys this repository's
PUSH-AUDIT.md to hunkydory as part of the local tooling it adopts,
since it is a template the fleet copies, so by the time this phase
runs hunkydory has a runbook and its pull requests are audited
against develop like any other repository's. For 33fl, this phase
runs this repository's PUSH-AUDIT.md briefs over the runner diff
directly and records that it did so, rather than citing an audit
nothing obliges anyone to perform.
Agent guidance¶
Execution model¶
All implementation work is done by sub-agents, never in the
management session, per the subagent-execution-model shared block
in PLAN-TEMPLATE.md. The management session plans, reviews the
actual files rather than the summary, and commits.
Planning effort¶
Phase 3 is high effort: it changes what the fleet is measured
against, and docs/consistency-audits.md warns that a wrong
criterion files issues in ten repositories rather than producing a
red build. Phase 4 is high effort for the same reason turned
outward -- it changes the machine every static job runs on, in two
organisations. Phases 1, 2, 5, 6 and 7 are medium: they follow
patterns already worked out elsewhere in the fleet.
Step-level guidance¶
| Step | Effort | Model | Isolation | Brief for sub-agent |
|---|---|---|---|---|
| 1 | medium | sonnet | none | Add hunkydory to the matrix in .github/workflows/consistency-audit.yml and to the in-scope list in docs/audits/README.md, and confirm it is absent from the excluded list. Those are the three statements audit/scope.py parses and AuditScopeIsStatedOnceTest holds them to each other, so all three change together. Do not add a REPO_OVERRIDES entry: per D4 the Python criteria already skip on the absence of pyproject.toml, and detect_repo_properties() has no per-criterion not-applicable key to carry a reason in. |
| 2 | medium | sonnet | none | In hunkydory: add Biome with a biome.json set to 100 columns, single quotes, semicolons; write tools/check-node.sh mirroring ryll's scripts/check-rust.sh; add .pre-commit-config.yaml calling it as a language: script hook alongside shellcheck, gitleaks and skillsaw; fix four relative links; align @types/node and add engines.node; copy PUSH-AUDIT.md in and reference it from AGENTS.md. |
| 3 | high | opus | worktree | Add three Check subclasses for npm dependency auditing to scripts/audit/checks/, following the worked brief in PLAN-TEMPLATE.md. Register in scripts/audit/registry.py, write a spec page each under docs/audits/, add them to the index in docs/audits/README.md, add their lines to FROZEN_METADATA, FROZEN_ISSUE_TITLES and the frozen column table in scripts/tests/test_metadata.py, and add tests in scripts/tests/test_packaging.py covering pass, fail and not-applicable. They must report not-applicable with a reason where there is no package.json, including against this repository -- hunkydory is the only repository in the fleet that has one. Read the phase 3 section for the five exemptions the dependency checks must carry (node builtins in both spellings, the host-provided vscode module, relative imports, @types/*, and devDependencies invoked from scripts); without them the first run files three false issues on hunkydory. |
| 4 | high | opus | worktree | Hold. See D5. Includes rewording the node half of the mermaid-lint rationale in the four files the phase 4 section names. |
| 5 | medium | sonnet | none | Copy the fleet workflow templates into hunkydory, including secret-scan.yml, substituting TypeScript for Python in CodeQL, and write ci.yml calling tools/check-node.sh on [self-hosted, static] with npm_config_cache under runner.temp, plus a job running pre-commit run --all-files. Read the phase 5 section for the four criteria that go live when .github/workflows/ first appears. |
| 6 | medium | sonnet | none | Deploy review tracking per docs/code-review-tracking.md, scoped to src/ and test/. |
| 7 | medium | sonnet | none | Hold. See D3, and the runner and secret-scoping constraints in the phase 7 section -- release.yml does not run on the static pool. |
| 8 | high | opus | none | Run PUSH-AUDIT.md over the accumulated diff, citing the other repositories' audits. |
Model choice¶
Per the subagent-model-roster shared block. Phases 3, 4 and 8 take
opus for the reasons in Planning effort; the rest are well-briefed
mechanical work where sonnet with the briefs above should succeed.
Management session review checklist¶
Per the plan-review-checklist shared block, plus this
repository's own checks:
-
pre-commit run --all-filespasses. -
python3 scripts/audit-check.py --repo-path . --repo-name developmentstill reports what it reported before the change, or this plan says why the verdict moved. Phase 3 is expected to add three not-applicable results here and nothing else. - Issue filing was exercised with
--dry-runonly. -
python3 scripts/audit-check.py --repo-path <hunkydory clone> --repo-name hunkydory --github-org shakenfistwas re-run after the phase, and the verdict moved the way the phase said it would. The Situation section established the start state by measurement; a phase that closes criteria should establish the end state the same way rather than asserting it. Phase 5 in particular moves four criteria from not-applicable to live, and phases 2 and 5 closellm-context-lint-cibetween them, so "which criteria changed" is the check that catches a phase claiming more than it delivered.
Risks and mitigations¶
The material below is stated in the phases too; it is gathered here because somebody deciding whether to approve phase 4 should not have to reassemble it from three sections.
Trixie regressions the runner phase has not verified. Phase 4
moves the static runner image from debian:12 to debian:13 without
having confirmed that the playbook's yq install via pip
--break-system-packages, the shared docker.yml, the claude CLI
install, or python3-venv and the rest of the base package list
behave the same on trixie. Mitigation: those four are named as
must-verify in phase 4 and are a precondition for proposing the
change, not a follow-up. The gradual rollout means a bad image
surfaces on one runner rather than all of them.
A mixed node-18 and node-20 pool for about a week. Line 229 is
inside the loop over missing_runners, so only newly created
instances get the new image and the weekly retire-and-rebuild cycle
replaces the fleet over roughly a week. A job could land on either.
Mitigation: hunkydory was verified to build and pass its 20 tests
on Debian 12's node 18.20.4, so both halves of the pool can run it;
and phase 5 waits for the rollout to finish rather than for phase 4
to land. "Landed" and "rolled out" are a week apart and the plan says
so.
Phase 3's criteria are measured against the whole fleet the next
morning. A wrong criterion files issues in twenty repositories
rather than producing a red build. Mitigation: the survey in phase 3
establishes that hunkydory is the only repository with a
package.json, so the blast radius today is one repository and three
issues. The five exemptions in phase 3 are the guard against those
three being false. Issue filing is exercised with --dry-run only,
per the review checklist.
Another session is editing 33fl concurrently (D5), so phase 4
could collide with work in flight. Mitigation: phase 4 is marked
Blocked in the Execution table, restated as Hold in the step
guidance, and carries "Do not execute without the operator's say-so"
at the head of its section. Nothing else in the plan writes to that
repository.
Phase 7 handles a Marketplace publish token. VSCE_PAT can
publish under the shakenfist publisher id. Mitigation: the
constraints in phase 7 -- an ephemeral runner, a tag-protected
environment secret, and build separated from publish so npm ci
never runs in the job that holds the token.
Administration and logistics¶
Success criteria¶
We will know when this plan has been successfully implemented because the following statements will be true:
scripts/audit-check.pyagainsthunkydoryreports no failures other thanreview-coverage, which phase 6 deliberately opens and which stays open until the human review queue has been worked.scope-coveragepasses againstdevelopmentagain.pre-commit run --all-filespasses in both repositories.- The three npm criteria have every part in step: the check, its
registration in
scripts/audit/registry.py, the specification underdocs/audits/, the line indocs/audits/README.md, their lines inFROZEN_METADATA,FROZEN_ISSUE_TITLESand the frozen column table inscripts/tests/test_metadata.py, and tests inscripts/tests/test_packaging.py. - No
REPO_OVERRIDESentry was needed, or any that was added carries a stated reason. - A push to
hunkydoryrunsnpm ciand its tests on a static runner without a node download. - hunkydory appears on the VS Code Marketplace, or phase 7 records why it does not.
- The human review queue for hunkydory is live and the operator can
work through
src/andtest/.
Documentation index maintenance¶
One row added to docs/plans/index.md, dated 2026-09-11, linking
this plan, with a one-line intent and a status from the shared
vocabulary. The row carries the whole-plan status and reaches
Complete only once every phase has completed, been abandoned or
been superseded.
Future work¶
- A second TypeScript repository is the point to generalise from:
whether Biome stays the choice, whether
tools/check-node.shbecomes a template undertemplates/, and whether the npm dependency criteria need to handle workspaces or monorepos. release-processmeasures a Python package. A TypeScript arm -- or a language-neutral restatement -- is worth considering once there is more than one non-Python release to describe.- The GitLab docker executor image at
33fl/static_runner.yml:630is stilldebian:12after phase 4. Somebody should decide whether it follows. - hunkydory's
test/corpus.tspoints by default at a siblingkerbside-patchescheckout, so the corpus check's verdict depends on which branch that checkout happens to be on. It degrades gracefully and says so, but a fixture inside the repository would be better. recount-patch.pyinkerbside-patchesand hunkydory'ssrc/diff.tsimplement the same counting rules in two languages. They were verified to agree on 175 patches, but nothing keeps them agreeing.
Bugs fixed during this work¶
Two patches in kerbside-patches were found to have hunk headers
that disagreed with their bodies while the counting rules were being
developed: patch137-horizon-requires-setuptools.patch was rejected
outright by git apply with "corrupt patch at line 25", and
patch097-kolla-ansible-fixed-proxy-cert.patch carried a trailing
context line git was silently ignoring. Neither was referenced by an
ORDER file. Both were corrected in
shakenfist/kerbside-patches#1683, which is where the recounter
itself landed.
No issue tracker entries in this repository relate to this plan;
scope-coverage's failure against development is expected to
arrive as an audit-filed issue rather than a hand-written one, and
phase 1 closes it.
Back brief¶
Before executing any step of this plan, please back brief the operator as to your understanding of the plan and how the work you intend to do aligns with that plan.