feat(gatorwalk-factory): a factory reads its definition from a file in the repo (swamp-club #2803) #386

Merged
seth merged 3 commits from cue/2803-gatorwalk-factory-lifecycle into main 2026-09-30 18:21:51 +00:00
Owner

swamp-club #2803: a factory reads its definition from a file in the repo.

What changes

  • A factory's globalArguments are now { definition: <repo-relative path> } (factories/<name>.yaml by convention) instead of the definition pasted inline. There is one copy of each definition, in the repo.
  • _lib/engine/definition_file.ts resolves the path against repoDir and refuses, naming the path: absolute, not .yaml/.yml, outside the repo (lexically or through symlinks), missing, or not a file. Away from a swamp repo (a remote worker) it says to start the work item where the repo is. Reads and creates go through the resolved path.
  • validate, design_page, new_key, start, reset repin=true and the tracker's claim read the file through loadFactoryDefinition, whose name and signature are unchanged. Pinning is unchanged: editing the file never changes a running work item.
  • New init --input from=<example> copies any of the skill's examples to the path and never overwrites. The examples are embedded by deno task gen:starters, because swamp bundles models and there is no manifest until go-live; scripts/gen_starters_test.ts guards against drift.
  • definition_file.ts uses pinned jsr: imports: swamp bundles reports without the import map, and the work-item-summary report imports the engine.
  • Tests: the fake gains an in-memory repo with symlinks, and every direct factory definition in the tests moves to its factory() helper. The integration harness creates factories with --global-arg. New integration cases cover the acceptance criteria, and the skill's worked example runs init as written.
  • Docs: the skill (driving.md, the worked example), README, and DESIGN.md ("Where a factory definition lives", decision log). ${{ stays rejected until go-live settles it.

Deviations from the issue

  • The key is definition and the directory factories/, following #2785's rename, not lifecycle/lifecycles/.
  • The starters are embedded, not resolved relative to the model module, because import.meta.url points into .swamp/ bundles.

Verification

verify-build 7e5da72c (12 passed, 2 skipped by guard, 0 failed) and verify-reviews ad14c844 (code review and adversarial review both pass); attestation 86abaf81 for f34785b79. Low findings left open: another process could plant a symlink between init's containment check and its write (it would need a hostile local process running at the same time), and one test assertion matches only a prefix of the starter list.

🤖 Generated with Claude Code

swamp-club #2803: a factory reads its definition from a file in the repo. ## What changes - A factory's `globalArguments` are now `{ definition: <repo-relative path> }` (`factories/<name>.yaml` by convention) instead of the definition pasted inline. There is one copy of each definition, in the repo. - `_lib/engine/definition_file.ts` resolves the path against `repoDir` and refuses, naming the path: absolute, not `.yaml`/`.yml`, outside the repo (lexically or through symlinks), missing, or not a file. Away from a swamp repo (a remote worker) it says to start the work item where the repo is. Reads and creates go through the resolved path. - `validate`, `design_page`, `new_key`, `start`, `reset repin=true` and the tracker's `claim` read the file through `loadFactoryDefinition`, whose name and signature are unchanged. Pinning is unchanged: editing the file never changes a running work item. - New `init --input from=<example>` copies any of the skill's examples to the path and never overwrites. The examples are embedded by `deno task gen:starters`, because swamp bundles models and there is no manifest until go-live; `scripts/gen_starters_test.ts` guards against drift. - `definition_file.ts` uses pinned `jsr:` imports: swamp bundles reports without the import map, and the work-item-summary report imports the engine. - Tests: the fake gains an in-memory repo with symlinks, and every direct factory definition in the tests moves to its `factory()` helper. The integration harness creates factories with `--global-arg`. New integration cases cover the acceptance criteria, and the skill's worked example runs `init` as written. - Docs: the skill (`driving.md`, the worked example), README, and DESIGN.md ("Where a factory definition lives", decision log). `${{` stays rejected until go-live settles it. ## Deviations from the issue - The key is `definition` and the directory `factories/`, following #2785's rename, not `lifecycle`/`lifecycles/`. - The starters are embedded, not resolved relative to the model module, because `import.meta.url` points into `.swamp/` bundles. ## Verification verify-build `7e5da72c` (12 passed, 2 skipped by guard, 0 failed) and verify-reviews `ad14c844` (code review and adversarial review both pass); attestation `86abaf81` for f34785b79. Low findings left open: another process could plant a symlink between `init`'s containment check and its write (it would need a hostile local process running at the same time), and one test assertion matches only a prefix of the starter list. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
A factory's globalArguments are now { definition: <repo-relative path> },
factories/<name>.yaml by convention, instead of the definition pasted inline.
One copy of each definition lives in the repo; nothing drifts.

- _lib/engine/definition_file.ts resolves the path against repoDir and refuses,
  naming the path, an absolute path, a non-.yaml/.yml path, one outside the
  repo (lexically or through symlinks) and a missing file; away from a swamp
  repo (a remote worker) it says to start the work item where the repo is.
- validate, design_page, new_key, start, reset repin=true and the tracker's
  claim read the file through loadFactoryDefinition; pinning is unchanged.
- New init method copies a starter (any of the skill's examples) to the path
  and never overwrites. The starters are embedded by deno task gen:starters,
  since swamp bundles models and there is no manifest until go-live; a test
  guards against drift.
- The fake gains an in-memory repo with symlinks; the integration harness
  creates factories with --global-arg; the skill, README and DESIGN.md
  ("Where a factory definition lives", decision log) are updated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
From the verify-reviews suggestions: writeNewDefinitionFile refuses a broken
symlink or non-directory ancestor with the definition path, and initFactory
looks starters up with Object.hasOwn.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fix(gatorwalk-factory): refuse a file-as-parent on a real disk and read and write through the resolved path (swamp-club #2803)
All checks were successful
CI / Review Integrity (pull_request) Successful in 1m4s
CI / Validate Attestation (pull_request) Successful in 1m8s
f34785b793
From the second verify-reviews pass: Deno.lstat throws NotADirectory, not
NotFound, when a parent is a regular file, so init surfaced a raw OS error;
treat it as nothing there. Read and create through the resolved path so a
symlink swapped after the check cannot redirect them. An integration case
covers the file-as-parent refusal on the real engine. DESIGN.md rewrap.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seth merged commit ff6484cc2b into main 2026-09-30 18:21:51 +00:00
seth deleted branch cue/2803-gatorwalk-factory-lifecycle 2026-09-30 18:21:52 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
swamp-club/swamp-extensions!386
No description provided.