Skip to content

Config: the factory's machine config and a repo's factory block - #37

Merged
tomkaczocha merged 4 commits into
mainfrom
t3/factory-issue-11
Oct 15, 2026
Merged

tomkaczocha merged 4 commits into
mainfrom
t3/factory-issue-11

Conversation

@rawphp

@rawphp rawphp commented Oct 15, 2026 •

Copy link
Copy Markdown

Closes #11

Contract

  • done: apps/server/src/factory/config.ts parses the machine config (<base>/factory/config.yml) and a .dw/config.yml factory block with softfac's fields, defaults and messages; an unknown machine key is an error that names it; a non-empty required_checks is required_checks is not supported yet; both loaders read the file again on every call; a missing machine config makes FactoryLayer log no factory config at <path> once per server start and stay idle; softfac's project-block tests from src/config.rs (pinned bf273a1) are ported and pass.
  • out: using the config in passes and claims, the Linear tracker, the users block, self_update/fleet/retro, warnings for unknown project keys, caching.
  • risk: none
  • test: vp test run apps/server/src/factory → pass (5 files, 104 tests)

Readings the issue left open

  • max_concurrent_runs: the issue says "softfac's defaults" for it and max_concurrent_gates, but softfac has no default for max_concurrent_runs (required, at least 1). The port keeps softfac's rule: max_concurrent_runs: is missing when absent. max_concurrent_gates defaults to 2. If a default of 1 was meant, it is one line in machineFrom.
  • Unknown machine key: one file-level error per key, <file>: unknown key <dotted path>, after the field errors, at the top level and inside each repos[i] (softfac's warning wording, as an error).
  • An invalid machine config logs each formatted error at error level and the factory stays idle (the issue only names the missing-file case).
  • softfac's project_required_checks_are_read_in_order is replaced by the not-supported case; [] still loads.

Acceptance criteria

  • config.ts reads <base>/factory/config.yml with gh_config_dir, poll_seconds 60, max_concurrent_runs, max_concurrent_gates 2, model_instance claudeAgent, model, repos of {repo, path}; an unknown key is an error that names it — config.test.ts "machine valid resolves every field and the defaults", "machine unknown top-level key is one file-level error", "machine unknown key inside a repo is named by its path"; the path comes from ServerConfig.baseDir in t3/paths.test.ts
  • A missing config.yml leaves the factory idle and logs no factory config at <path> once per server start — index.test.ts "logs that there is no config when the machine file does not exist" (the layer is built once per start)
  • A function parses .dw/config.yml text into the factory block with softfac's fields and defaults (minutes 180, gate_retries required, regates 5, checks_minutes 30, interruption_resumes 2) and softfac's messages — parseProjectConfig; config.test.ts "project valid resolves all fields" and the ported // ---- project: sections
  • A non-empty required_checks is required_checks is not supported yet — config.test.ts:204
  • Both are read again on every call; nothing is cached — config.test.ts "loadMachineConfig reads the file on every call", "loadProjectConfig reads the file on every call"
  • Uses the yaml package apps/server already depends on, no new dependency — package.json and the lockfile are unchanged in this branch
  • softfac's project-block cases from src/config.rs are ported and pass — one it per Rust #[test] in config.test.ts; the correctness reviewer checked each has a counterpart of equal or stronger strength

Gate

  • vp test run apps/server/src/factory → exit 0
  • vp run --filter t3 typecheck → exit 0

Models

  • maker: claude-sonnet-5-5 (host)
  • checker: claude-opus-5-5 (host)

Review

  • cycles: 1
  • personas: correctness (approve, 0 findings), maintainability (approve, 1 nit, fixed in 7265e29d6), testing (approve; 11 of 12 named mutants killed, the twelfth was equivalent as written and its intended form was killed too)
  • fixed: ConfigError and the ConfigErrors schema defined the same shape twice → one ConfigErrorSchema
  • leftovers: two testing nits, dropped as out of scope: ConfigErrors.message is never asserted; the must be a list of strings message text is never asserted (the ported softfac tests assert only the field for that case)

Notes

  • Worktree was behind origin/main at launch (e4bcce9f6); fast-forwarded to f1dba9a17 before provisioning.
  • Red phase confirmed: at aa0abf236 the suite fails (3 of 5 files); green from 45a137e8f.
  • A first gh pr create without --repo opened the draft on the upstream parent (Config: the factory's machine config and a repo's factory block pingdotgg/t3code#19866); it was closed within the minute and nothing else was written there. This PR is the one that counts.
  • No seam touched. No upstream file outside apps/server/src/factory/ changed. All seven factory:* labels already existed; none created.

Lessons

  • Read the branch from the worktree, not from the launch context. Symptom: the first push failed with "src refspec does not match any". Cause: the session's git status snapshot named a branch (t3/ffe914e1) that was not the worktree's branch (t3/factory-issue-11). Default: at Isolate run git branch --show-current in the worktree and use that name for every push and the PR.

  • Pass --repo <owner/repo> to gh pr create on a fork. Symptom: the draft PR opened on the upstream parent repo and had to be closed. Cause: on a fork, gh resolves the parent as the default base repo even when origin is the fork. Default: always pass --repo naming the issue's repo on gh pr create and gh pr edit, and read the URL back before reporting.

Commits

  • aa0abf236 test(factory): port config parsing tests and the config-driven start — Make slice (red)
  • 45a137e8f feat(factory): load the machine config and start only when it is valid — Make slice (green)
  • 7265e29d6 refactor(factory): define the config error shape once — Check cycle 1

Files

  • apps/server/src/factory/config.ts — machine config and project factory block parsing, loaders
  • apps/server/src/factory/config.test.ts — ported softfac project cases, machine cases, loader cases
  • apps/server/src/factory/paths.ts — FactoryPaths service tag (factory-owned, no T3 import)
  • apps/server/src/factory/t3/paths.ts — FactoryPaths from ServerConfig.baseDir
  • apps/server/src/factory/t3/paths.test.ts — path derivation
  • apps/server/src/factory/index.ts — FactoryRuntime reads the config at build; FactoryLayer wires the paths
  • apps/server/src/factory/index.test.ts — the three start states

Red: config.test.ts, index.test.ts and t3/paths.test.ts fail because
./config.ts, ./paths.ts, t3/paths.ts and FactoryRuntime do not exist yet.

Issue: #11
Adds config.ts (project and machine config parsing ported from softfac,
plus loaders), the FactoryPaths service with its T3 layer, and makes the
factory log no-config, config errors, or started. Also fixes test-helper
slips in the ported tests (repos error path, 90.0 fixture, layer wiring).

Issue: #11
@rawphp
rawphp marked this pull request as ready for review October 15, 2026 11:27
@rawphp

rawphp commented Oct 15, 2026

Copy link
Copy Markdown
Author

Factory gate: passed (attempt 1, run by the factory in a fresh checkout of the pull request's head 7265e29 merged with the base branch tip b679ddd)

Command Exit Time
factory/scripts/check.sh 0 10.0 s

FactoryPaths keeps the adapter's shape from main (the service and its layer in
factory/t3/paths.ts) and gains machineConfigPath, which the config loader reads.
factory/paths.ts goes.
@tomkaczocha
tomkaczocha merged commit a4cdc63 into main Oct 15, 2026
@rawphp
rawphp deleted the t3/factory-issue-11 branch October 28, 2026 04:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Config: the factory's machine config and a repo's factory block

2 participants