Repository navigation
docs: split the README into audience-scoped docs and adopt repo-template scaffolding - #548
Conversation
…ate scaffolding README was 477 lines serving four audiences at once; 226 of them were release engineering. It is now 102 lines: what Cluckwork is, how to run it, where everything else lives. Moved verbatim, nothing dropped: - release how-to, deploy-by-digest and the repair paths to docs/releasing.md - production and dev bootstrap-admin to docs/runbooks/first-admin-provisioning.md - pg_dump/pg_restore to docs/runbooks/backup-and-restore.md - hooks, dev env, DB reset, schema-change workflow and commit messages to CONTRIBUTING.md Adopted from mforce/repo-template: - CONTRIBUTING.md, SECURITY.md, a PR template with this repo's own checks - runbook and decision-record templates, plus a runbooks index - .editorconfig, .gitattributes, an inert CODEOWNERS Also: docs/README.md maps the tree and states which file a rule belongs in. Cross-references in AGENTS.md, deploy/README.md and the decision records now point at docs/releasing.md. The two existing runbooks gained a Last drilled header, honestly set to "not recorded". Verified: every relative link in the touched files resolves, with the checker proven against an injected broken link first; every distinctive string from the old README still resolves somewhere in the tree.
|
@codex please review this PR. It is documentation-only apart from one comment line in |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1cdd2a1d02
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| — if someone pushed those bytes by hand, the digest is still a perfectly valid, | ||
| perfectly immutable digest. The attestation is a signed claim by this repo's CI | ||
| workflow, so anything pushed by hand has no such claim and fails the check. |
There was a problem hiding this comment.
Preserve the attested-byte substitution limitation
When an attacker can write to the registry and select an image digest that this workflow attested previously, manually substituting those bytes still presents a valid provenance claim, so “anything pushed by hand has no such claim” overstates the guarantee. The canonical boundary explicitly leaves substitution of other attested bytes open; qualify this as rejecting newly pushed, unattested bytes under a leaked registry credential.
AGENTS.md reference: AGENTS.md:L230-L235
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in d51744b. "Anything pushed by hand has no such claim" now reads: bytes CI never built carry no such claim and fail the check; bytes CI did build still verify whoever pushed them, so this does not stop an older attested image being substituted — which is what step 3 is for. That matches the canonical boundary in AGENTS.md, which this file summarises rather than restates. Also inherited verbatim from the old README.
| All three flags matter and none is the default — each narrows *whose* claim is | ||
| accepted (the registry copy, one workflow, one branch). Copy the command as-is. |
There was a problem hiding this comment.
Describe each attestation flag by its actual role
--bundle-from-oci does not narrow whose claim is accepted; it selects the registry copy of the attestation bundle rather than the GitHub API copy. Only --signer-workflow and --source-ref constrain signer identity, so grouping the registry source with “whose claim” contradicts the canonical explanation and gives deployers the wrong security model.
AGENTS.md reference: AGENTS.md:L227-L227
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in d51744b. The three flags are now described by role, matching the canonical bullet in AGENTS.md: --bundle-from-oci reads the attestation from the registry copy rather than the GitHub API, and only --signer-workflow and --source-ref narrow whose claim is accepted. The sentence was inherited verbatim from the old README, not introduced by the split.
| # * @OWNER | ||
|
|
||
| # Paths where a self-merge rewrites what every other check asserts. | ||
| # /.github/ @OWNER |
There was a problem hiding this comment.
Use recursive patterns before activating CODEOWNERS
If the documented activation steps are followed, /.github/, /.githooks/, and /docs/decisions/ do not recursively own files under those directories in CODEOWNERS syntax; directory contents require patterns such as /.github/**. Consequently workflow changes such as .github/workflows/ci.yml would remain ownerless even though this file says activation closes the self-merge control gap.
AGENTS.md reference: AGENTS.md:L236-L242
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Rejected, with evidence — a trailing-slash CODEOWNERS pattern is recursive.
GitHub's own CODEOWNERS syntax examples state it twice:
In this example, @doctocat owns any files in the
build/logsdirectory at the root of the repository and any of its subdirectories. —/build/logs/ @doctocat
In this example, @doctocat owns any file in the
/docsdirectory in the root of your repository and any of its subdirectories. —/docs/ @doctocat
So /.github/ would own .github/workflows/ci.yml once activated; /.github/** is not required. Leaving the patterns as they are.
(Restating for the record: the file ships inert — every pattern is commented out — because main currently has required_approving_review_count: 0 and one owner with write access, so activating it would be a deadlock or self-approval. The claim in the header is about what activation would close, not about anything this PR turns on.)
| # regenerating, never by hand-editing (#417 for the schema docs). | ||
| docs/schema/** linguist-generated=true | ||
| **/packages.lock.json linguist-generated=true | ||
| web/package-lock.json linguist-generated=true |
There was a problem hiding this comment.
Mark every generated npm lock file consistently
The repository also tracks tools/simulation/ui/package-lock.json, but this rule marks only web/package-lock.json as generated. Changes to the simulation UI lock file therefore remain in language statistics and expanded PR diffs despite the surrounding section describing generated lock files; use a repository-wide **/package-lock.json pattern or list the second lock explicitly.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in d51744b. tools/simulation/ui/package-lock.json is indeed tracked; the rule is now the repository-wide **/package-lock.json. Verified: git check-attr linguist-generated returns true for both lock files, both packages.lock.json files and docs/schema/.
…les generated Codex review round 1 on PR 548: - P2, docs/releasing.md: the summary said all three verify flags narrow whose claim is accepted. Only --signer-workflow and --source-ref do that; --bundle-from-oci selects the registry copy of the bundle. Now split by role, matching the canonical bullet in AGENTS.md. - P2, docs/releasing.md: "anything pushed by hand has no such claim" overstated the guarantee — bytes CI did build still verify whoever pushes them. Narrowed to bytes CI never built, and pointed at step 3 for the substitution case. - P3, .gitattributes: tools/simulation/ui/package-lock.json was not marked generated. Both lock files now match a repository-wide pattern. Both prose defects were inherited verbatim from the old README rather than introduced by the split.
|
@codex round 1 addressed in d51744b.
Both prose defects were inherited verbatim from the old 477-line README rather than introduced by the split — worth knowing, since the same two sentences were live on Please re-review. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d51744b05c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Start the API and confirm `/health/ready` returns 2xx — it 503s while any | ||
| migration is pending (#263), which is also how a restore of an older schema | ||
| announces itself. |
There was a problem hiding this comment.
Run migrations before restarting after an older restore
When the restored dump predates the current schema, starting the API does not bring it forward: Production sets Database:MigrateOnStartup=false, and the already-completed Compose migrate service is not rerun by restarting app. Readiness will therefore remain 503 rather than merely “announce” the stale schema, leaving the recovery incomplete; add an explicit one-shot migrate invocation before restarting the API.
AGENTS.md reference: AGENTS.md:L63-L63
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in 60fd07d. Verified against deploy/docker-compose.yml: migrate is a one-shot service with command: ["migrate"] that app waits on via service_completed_successfully, so a completed job plus Database:MigrateOnStartup=false means restarting app applies nothing and readiness stays 503 indefinitely.
The restore section now runs the job explicitly before starting the API:
docker compose -f deploy/docker-compose.yml --env-file deploy/.env run --rm migrateand the wording no longer implies the 503 clears on its own. The drill gained the older-dump case: restore a pre-migration dump, confirm readiness stays 503 until migrate has run.
…tore Codex review round 2 on PR 548, P2: the restore procedure said to start the API and treat a 503 as the schema announcing itself. It does not self-heal. Production sets Database:MigrateOnStartup=false and the Compose migrate job has already completed, so restarting app never applies the pending migration and readiness stays 503. The runbook now runs the one-shot migrate service before starting the API, and the drill covers the older-dump case.
|
@codex round 2 addressed in 60fd07d. Migrate before restarting after an older restore (P2) — confirmed real, fixed. Verified against Running total on this PR: 4 confirmed product/doc defects, 1 finding rejected with evidence. Please re-review. |
|
Codex Review: Didn't find any major issues. Another round soon, please! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
🤖 I have created a release *beep* *boop* --- ## [0.1.0](v0.0.4...v0.1.0) (2026-09-12) ### ⚠ BREAKING CHANGES * log in by farm code, with per-account email identity ([#532](#532)) (#564) ### Features * **accounts:** add Account.Slug (farm code), suspend/reactivate, list-accounts verb ([#531](#531)) ([3fe9754](3fe9754)) * **accounts:** provision additional farms ([#581](#581)) ([006f298](006f298)) * add Aspire local development AppHost ([#567](#567)) ([2c9e6b9](2c9e6b9)) * add configurable worker sale allocation ([#619](#619)) ([0955095](0955095)) * add searchable entity pickers ([#642](#642)) ([60d2053](60d2053)) * **api:** provision-account takes an optional --timezone at creation ([#603](#603)) ([#694](#694)) ([a0aee39](a0aee39)) * **audit:** show the sales-line audit payload as a readable Details column ([#745](#745)) ([#749](#749)) ([d26d389](d26d389)) * **auth:** add ApplicationUser.StepUpLogoutEpoch column ([#338](#338)) ([#554](#554)) ([18306ee](18306ee)) * certify over-cap simulation fixture bands ([#633](#633)) ([a67b2e1](a67b2e1)), closes [#627](#627) * **cli:** rename-account verb to change a farm code ([#732](#732)) ([#733](#733)) ([4b70559](4b70559)) * **customers:** edit existing customer details ([#625](#625)) ([#626](#626)) ([062a55c](062a55c)) * **jobs:** single-runner leader gate for the durable job worker ([#271](#271)) ([#555](#555)) ([4148f9b](4148f9b)) * let owners change user email addresses ([#605](#605)) ([842347b](842347b)) * log in by farm code, with per-account email identity ([#532](#532)) ([#564](#564)) ([68adb62](68adb62)) * **ratelimit:** distributed IP-keyed auth limiters ([#544](#544)) ([#558](#558)) ([ec14972](ec14972)) * **ratelimit:** distributed per-account report concurrency cap with local-ceiling fallback ([#545](#545)) ([#559](#559)) ([1522e4e](1522e4e)) * **sales:** mark discounted lines, total the discount, and show it in the Orders list ([#723](#723), [#724](#724)) ([#741](#741)) ([1a07441](1a07441)) * **sales:** record list, old and new price in the order-line audit payload ([#722](#722)) ([#742](#742)) ([97c866f](97c866f)) * **sales:** refuse an over-ceiling confirm from a Sales user ([#727](#727)) ([#766](#766)) ([8c0792a](8c0792a)) * **sales:** show what each order still owes, and filter the list to unpaid ([#771](#771)) ([ca59d68](ca59d68)) * **sales:** snapshot the list price on the order line and show the discount ([#734](#734)) ([cffed5e](cffed5e)) * **sales:** snapshot the product name and unit in the order-line audit payload ([#747](#747)) ([#748](#748)) ([0481c06](0481c06)) * scope Worker reads to assigned flocks ([#388](#388)) ([#611](#611)) ([5884a9a](5884a9a)) * shared-state ports with Redis + in-process fallback ([#543](#543)) ([#552](#552)) ([f767fa9](f767fa9)) * suspend-account / reactivate-account operator verbs ([#534](#534)) ([#573](#573)) ([d0be26c](d0be26c)) * **tenancy:** write-side tenant guard + single-assignment TenantContext ([#546](#546)) ([#561](#561)) ([f371f1d](f371f1d)) * **web:** dashboard rework — capture-status tiles, 14-day trend, stock as a stacked bar ([#654](#654)) ([396ba23](396ba23)) * **web:** date-range filters on audit and expenses, and the stock lot filter gets its bounded toolbar ([#666](#666), [#667](#667), [#653](#653)) ([94b188f](94b188f)) * **web:** elevation hierarchy and sentence-case labels ([#651](#651), [#652](#652)) ([#661](#661)) ([28db4c7](28db4c7)) * **web:** Expenses and Audit keep a clear-filters control while rows are still showing ([#679](#679)) ([#697](#697)) ([b859982](b859982)) * **web:** expenses filters by a date range like its sibling screens ([#667](#667)) ([f13858f](f13858f)) * **web:** key the farm brand palette per farm ([#586](#586)) ([#600](#600)) ([7183a43](7183a43)) * **web:** let operators forget remembered farms ([#598](#598)) ([577d94e](577d94e)) * **web:** one-line provenance, bounded date filters, and empty states that invite action ([#653](#653), [#655](#655)) ([#668](#668)) ([80b53f4](80b53f4)) * **web:** prefill the farm code from ?farm= and remember it ([#535](#535)) ([#588](#588)) ([b7f5cc6](b7f5cc6)) * **web:** split authenticated routes into lazy chunks ([#620](#620)) ([5089271](5089271)) * **web:** the audit log filters by a date range, and says which window is empty ([#666](#666)) ([63027e0](63027e0)) * **web:** typeset numbers as numbers and refresh the Help glossary ([#650](#650), [#657](#657)) ([af4fe11](af4fe11)) ### Bug fixes * **api:** order same-instant audit events by a durable monotonic key ([#700](#700)) ([8fcf084](8fcf084)) * **api:** print the farm code from bootstrap-admin ([#589](#589)) ([#594](#594)) ([34032ac](34032ac)) * **audit:** show the price a line sold for, not its list price ([#759](#759)) ([e6b37d0](e6b37d0)) * **audit:** store catalog enums by name and guard the add-item transaction shape ([#751](#751)) ([23609ff](23609ff)) * **auth:** reject invalid account claims ([#622](#622)) ([8d6c7fe](8d6c7fe)) * **auth:** require step-up for durable user access ([#360](#360)) ([#607](#607)) ([f767dce](f767dce)) * **ci:** bound the npm audit calls and give the web job room to finish ([#686](#686)) ([153b7a8](153b7a8)) * **ci:** escalate the audit bound to SIGKILL, so it actually bounds ([#686](#686)) ([a0c8f4e](a0c8f4e)) * **ci:** fail closed on invalid vulnerability config ([#621](#621)) ([1690db8](1690db8)) * **ci:** lockfix covers the two AppHost lock files, derived from the sln ([efb05e6](efb05e6)) * **ci:** lockfix covers the two AppHost lock files, derived from the sln ([8986d77](8986d77)) * **ci:** remove invalid XML comment from nuget.lockfix.config ([#541](#541)) ([5f1bc0a](5f1bc0a)) * **ci:** the advisory vuln gate no longer blocks on an unusable report ([#686](#686)) ([aaf6934](aaf6934)) * **ci:** the advisory vuln gate no longer blocks on an unusable report ([#686](#686)) ([64f1f53](64f1f53)) * **i18n:** tl help text names the saleable flag and unit-system setting what their labels call them ([#688](#688)) ([#696](#696)) ([bfd24d7](bfd24d7)) * **infra:** AccountId must be a non-nullable Guid or both tenant write layers refuse ([#673](#673)) ([#695](#695)) ([2470c4e](2470c4e)) * require step-up for flock scope changes ([#609](#609)) ([4151f89](4151f89)) * **sales:** keep a line's discount markers agreeing while its price is edited ([#752](#752)) ([#753](#753)) ([c159b4b](c159b4b)) * **sales:** say which kind of missing list price a line has ([#774](#774)) ([489180e](489180e)) * scope legacy logout to selected farm ([#624](#624)) ([fae8d82](fae8d82)) * **seed:** drain the daily-entry lock sweep so deep simulation fixtures validate ([#644](#644)) ([730fa23](730fa23)), closes [#638](#638) * **tenancy:** AccountId is a concurrency token, so the database refuses a detached cross-tenant write ([#562](#562)) ([4d1dfa3](4d1dfa3)) * **tenancy:** AspNetUserRoles carries a tenant column, so a role write naming another farm's user is refused ([#670](#670)) ([fc0552a](fc0552a)) * **tests:** bump the image-pin allow-list counts for the AppHost LocalPorts tests ([#593](#593)) ([58d3056](58d3056)) * **tests:** the OTLP collector survives a lost port race and ignores traffic that is not an export ([#672](#672), [#676](#676)) ([#677](#677)) ([965c737](965c737)) * **web:** a scoped audit view filtered to nothing names both the record and the range ([#666](#666)) ([41bbfe1](41bbfe1)) * **web:** an abandoned dialog attempt's success no longer hijacks the replacement on Customers, Daily Entry, Flocks, Grades and Products ([#703](#703)) ([#705](#705)) ([85605db](85605db)) * **web:** an abandoned dialog attempt's success no longer hijacks the replacement on Inventory, Expenses, History and Stock ([#703](#703)) ([#706](#706)) ([60a4997](60a4997)) * **web:** an abandoned edit's success no longer hijacks the dialog that replaced it on Users ([#703](#703)) ([#710](#710)) ([778faab](778faab)) * **web:** an abandoned order attempt's success no longer hijacks the dialog that replaced it ([#702](#702)) ([522c699](522c699)) * **web:** capture screens open on the flock you last used, and assigning one no longer guesses ([#646](#646)) ([#699](#699)) ([7f8f317](7f8f317)) * **web:** constrain dialog session helpers to declared scopes ([#715](#715)) ([389e3c8](389e3c8)) * **web:** date validation gets one boundary table instead of one case per review round ([#666](#666)) ([215f830](215f830)) * **web:** keep a paged window and an item panel on the user's newest intent ([#645](#645)) ([d81bccf](d81bccf)) * **web:** keep Sales order panels closed after pending writes ([#711](#711)) ([f0f7492](f0f7492)) * **web:** keep Sales panels closed after pending Open reads ([#716](#716)) ([620411f](620411f)) * **web:** make login take the cross-tab cookie lock so a racing refresh cannot restore the wrong session ([#648](#648)) ([ff18beb](ff18beb)) * **web:** make the entity picker read as a search field and focus it on open ([#736](#736)) ([66ef667](66ef667)), closes [#735](#735) * **web:** page truncated customer and movement tables with usePagedList ([7cfe4d6](7cfe4d6)) * **web:** reconcile Sales line edits with refreshed orders ([#717](#717)) ([d7dd2c9](d7dd2c9)) * **web:** the audit date filter accepts low-numbered years, and its empty state covers every narrowing ([#666](#666)) ([af52d25](af52d25)) * **web:** the audit date filter rejects impossible dates, and its history guard actually guards ([#666](#666)) ([8d51846](8d51846)) * **web:** the expense range bounds are not capped at today, which the month-end default exceeds ([#667](#667)) ([7e01864](7e01864)) * **web:** the help text calls the expiry field what the field calls itself ([#666](#666)) ([2fd1f3c](2fd1f3c)) * **web:** the stock lot date range sits in the bounded toolbar ([#653](#653)) ([43dec5e](43dec5e)) ### Refactoring * **web:** extract SalesPage's dialog-write wrapper into a shared useDialogAction hook ([#703](#703)) ([#704](#704)) ([60ee9d9](60ee9d9)) ### Documentation * add k6 preparation steps to the dev-database fixture runbook ([#643](#643)) ([a4f1f09](a4f1f09)) * add runbook for loading the simulation fixture into a dev database ([#639](#639)) ([2d143b8](2d143b8)) * **agents:** a PR closes its issue from the body, not the title ([#744](#744)) ([39be13c](39be13c)) * **agents:** drop the commit and push gate, and require screenshots on UI changes ([#757](#757)) ([6225172](6225172)) * **agents:** find guards by grepping registry readers; amend issues a PR overtakes ([#580](#580)) ([fe3fde8](fe3fde8)) * **agents:** the Playwright specs have been in CI since 2026-08-08 ([#768](#768)) ([68ee612](68ee612)) * **aspire:** record the second local database and pin the AppHost dashboard ports ([#623](#623)) ([713b941](713b941)) * compress AGENTS.md to one paragraph per rule, and draw the two orders that matter ([#551](#551)) ([997ae8a](997ae8a)) * item 7 names each screen's actual initial filter value ([#666](#666)) ([70a53d8](70a53d8)) * multi-farm tenancy decision record and AGENTS/GLOSSARY sync ([#537](#537)) ([#601](#601)) ([2c34771](2c34771)) * name the scoped filtered-empty key and state the [#653](#653) relationship plainly ([#666](#666)) ([0e93dac](0e93dac)) * note that a PackageReference in Directory.Build.props is invisible to the dependency graph ([4845724](4845724)) * **plans:** commit the [#722](#722) and [#745](#745) design records ([#754](#754)) ([c942fcd](c942fcd)) * record [#579](#579) as won't-fix — suspension is immediate for use, not issuance ([#582](#582)) ([7a3be40](7a3be40)) * record the [#508](#508) audit ordering key and the tracked-file guard lesson ([#701](#701)) ([08964e9](08964e9)) * **runbooks:** add procedure to rename the default farm's code after upgrade ([#731](#731)) ([2f6e242](2f6e242)) * screenshots of the running SPA in the README ([#550](#550)) ([711488a](711488a)) * **sim:** commit the dashboard screenshot, capture the palette matrix, and record the [#651](https://github.com/mforce/cluckwork/issues/651)/[#652](https://github.com/mforce/cluckwork/issues/652) conventions ([#660](#660), [#662](#662), [#663](#663), [#664](#664)) ([#665](#665)) ([930ea30](930ea30)) * specify searchable entity picker ([#641](#641)) ([91d4300](91d4300)) * split the README into audience-scoped docs and adopt repo-template scaffolding ([#548](#548)) ([b3f3fcf](b3f3fcf)) * surface Aspire local development workflow ([#568](#568)) ([a343baa](a343baa)) * **web:** record the per-screen idempotency-key policies and runWrite's refresh contract ([#703](#703)) ([#707](#707)) ([8bee651](8bee651)) * **web:** the date-cap help text covers every stocked item, not only feed ([#666](#666), [#667](#667)) ([c8433c5](c8433c5)) * **web:** the help text claims only what is true of recording, and says nothing about filter caps ([#666](#666), [#667](#667)) ([e2f63d1](e2f63d1)) * **web:** the help text describes the date-range filters that shipped ([#666](#666), [#667](#667)) ([c3275b7](c3275b7)) * **web:** the help text stops describing a cap the filters no longer have ([#666](#666), [#667](#667)) ([49654cd](49654cd)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: cluckwork-lockfix[bot] <309265648+cluckwork-lockfix[bot]@users.noreply.github.com>
What and why
README.mdwas 477 lines serving four audiences at once — evaluator, developer, release engineer, operator. 226 of those lines (47%) were release engineering. It is now 102 lines: what Cluckwork is, how to run it, and where everything else lives.Nothing is deleted. Every block moved to a file whose audience matches it:
docs/releasing.mdbootstrap-admindocs/runbooks/first-admin-provisioning.mdpg_dump/pg_restoredocs/runbooks/backup-and-restore.mdCONTRIBUTING.mdAdopted from
mforce/repo-templateCONTRIBUTING.md— the human path;AGENTS.mdstays canonical and this links to it rather than restating rationale.SECURITY.md— reporting, supported versions, what CI enforces, the dated-exception mute..github/pull_request_template.md— the template's checklist plus this repo's own checks (Version++, chore(schema): generate PostgreSQL schema documentation #417 schema docs, Sim harness (#243) rotted silently: 4 breakages, no CI ever ran it #370 sim harness, epic sync).docs/runbooks/TEMPLATE.md+ index,docs/decisions/TEMPLATE.md..editorconfig,.gitattributes, an inertCODEOWNERS.Notes on the three config files
.gitattributes—* text=auto eol=lf. Confirmed zero CRLF files tracked, so it causes no renormalisation churn. It protects.githooks/*andtools/simulation/*.shfrom a CRLF checkout, where the kernel looks for/bin/sh\rand fails naming neither the hook nor the cause.CODEOWNERSships inert.maincurrently hasrequired_approving_review_count: 0with a single owner holding write access, so "Require review from Code Owners" would be a deadlock or self-approval theatre. The file records the gap it would close — a merge tomainrewritingci.ymlstill produces a genuinely valid attestation — and the three steps to activate.SECURITY.mdstates plainly that no private reporting channel is enabled, rather than pointing at GitHub private vulnerability reporting, which is off. Owner call this session. It carries a maintainer note on how to turn it on.Cross-references updated
AGENTS.md(release how-to pointer ×2, plus human entry points at the top),docs/decisions/README.md,docs/decisions/351-releases.md,deploy/README.md, and a stale(README)comment inSeedCommandTests.cs. The two existing runbooks gained a Last drilled header, honestly set tonot recorded— no drill was performed, so none is dated.How it was verified
42P07,Address already in use,cluckwork-dev_cluckwork-dev-pg18,--bundle-from-oci,--signer-workflow,--source-ref, theword((parser trap,Release-As: 1.0.0,[skip ci], …) still resolves somewhere in the tree. First run reported three false misses becausegrep -Fate the leading--as options; fixed with-eand re-run clean.Checklist