Repository navigation
[0.0.14] — 2026-05-08
Theme: v0.0.13 recovery — 3 must-fix correctness bugs + 6 scaffold/CLI ergonomics polish. v0.0.13's URL-shortener BASELINE surfaced 3 hard regressions (apostrophe-strip phantom no-converge, workspace:* shipped to npm, factory spec review broken on published packages) plus 6 honorable-mention polishes. v0.0.14 ships all 9 in 8 sibling specs — 7/8 first-iter, 1/8 agent-exit-nonzero with work landed (same v0.0.11 worktree-spec friction shape; the implementation is correct).
Added
- Harness regex-no-match safety net (factory-harness). When
bun test -t <pattern>exits nonzero AND its output containsregex .+ matched 0 tests, the runner classifies the satisfaction asstatus: 'error'with detail prefixharness/test-name-regex-no-match: <pattern> matched 0 tests in <file>instead of'fail'. The runtime treatserroras a tooling-mismatch signal (not as "implementation broken — re-implement"), which prevents the v0.0.13 phantom-no-converge loop. Mirrors v0.0.13's coverage-trip detection shape. factory finish-task --all-convergedstatus-aggregator (factory-core). Walks each candidate factory-run's descendantfactory-phaserecords, finds the FINAL iteration's terminal phase, and only ships whenterminalPhase === 'pass'. Skipped specs surface asfactory: skipped <id> (run <runId> did not converge — last phase: <status>). The all-no-converge sequence case is a safe no-op (exit 0, shipped=0). Closes the v0.0.13 BASELINE friction where finish-task disagreed with run-sequence on what "converged" means.resolveContextDirhelper (factory-core). Pure function exported from@wifo/factory-corethat resolves the precedence chaincliFlag → configValue → './.factory'. Used byfactory finish-task; mirrored infactory-context. Public API surface delta:@wifo/factory-core34 → 35 (+1).- Day-zero scaffold passes its own DoD gates (factory-core).
factory initnow writes a stubsrc/index.ts(export const VERSION = '0.0.0';) +src/index.test.ts(1 test) sopnpm typecheck && pnpm test && pnpm checkall exit 0 on the freshly-scaffolded tree. Single-lineBIOME_JSON_TEMPLATEarrays so biome's lineWidth=100 rule doesn't trip on the template's own format. The agent overwrites the stubs on first feature scope. - Lint code
spec/yaml-colon-needs-quoting(factory-core). Severity warning. Detects unquoted frontmatter values containing:(which YAML parses as a nested mapping) and emits a friendly fix-suggestion ("wrap the value in single quotes")./scope-project's authoring rules updated to auto-quote — prevents the bomb at scoping time so the lint warning rarely fires in practice. --setting-sources project,localonclaude -pspawns (factory-runtime). Excludes user-level~/.claude/settings.jsonglobal plugin/skill auto-suggestion hooks (e.g., the Vercel/Next.js skill injection the v0.0.13 implement-phase agent reported as false-positive noise). Subscription auth is preserved (OAuth/keychain credentials live outside settings.json). Project-level.claude/settings.jsonand*.local.jsonhooks still load — only user-level global noise is filtered.
Changed
normalizeTestNamePatternno longer strips apostrophes (factory-harness). v0.0.12's apostrophe-stripping (added to handle hypothetical agent stylization ofit()names) caused a real soundness bug: specs that genuinely used"slug's log"had the apostrophe stripped to"slugs log"before passing to bun's-tregex, but the actual test still had the apostrophe → bun matched 0 tests → 5 wasted iterations on phantom no-converge in the v0.0.13 BASELINE. The strip set is reduced to curly-double-quotes only; apostrophes (ASCII + curly) are now treated as literals. Modern Claude preserves apostrophes correctly in test names; the original motivation didn't manifest.factory spec reviewdispatches viachild_process.spawninstead ofcreateRequire(factory-core). v0.0.13'screateRequire(import.meta.url)workaround for the workspace cycle hit CJS resolution againstfactory-spec-review's ESM-only exports map → silent exit 0 on the npm-published artifact (the workspace symlink-bypass masked it). v0.0.14 spawns thefactory-spec-reviewbin as a subprocess; process boundary eliminates the type-resolution AND the CJS/ESM mismatch. Subscription auth preserved via env propagation. The factory-cli umbrella package option stays deferred for v1.0.0.--context-diruniversal default flips from./contextto./.factory(factory-core, factory-context).factory initcreates./.factory, so the previous split —factory-runtimedefaulted to./.factorywhilefactory finish-taskandfactory-contextdefaulted to./context— silently looked in the wrong directory. v0.0.14 unifies all three CLIs on./.factoryAND adds auto-detect fromfactory.config.json runtime.contextDir. Precedence: CLI flag (--context-dir) >factory.config.json runtime.contextDir> universal default./.factory. Breaking for users who relied on the old./contextdefault — pass--context-dir ./contextto keep the v0.0.13 behavior.- CI publish flow uses
pnpm pack+npm publish <tarball>two-step (workflow). v0.0.13.x's switch frompnpm publishtonpm publish(for OIDC handshake) lost pnpm's automaticworkspace:*→^<version>rewrite, breakingnpx @wifo/factory-core initfor fresh users. v0.0.14 splits the responsibilities:pnpm packdoes the manifest rewrite + produces a tarball;npm publish <tarball> --provenanceuploads via OIDC. Post-publish verification re-fetches each manifest and fails the workflow ifworkspace:leaks through — regression-pin against future shape regressions. - All six
@wifo/factory-*packages bumped to0.0.14in lockstep.init-templates.tsPACKAGE_JSON_TEMPLATE.dependenciesbumped from^0.0.13to^0.0.14for every@wifo/factory-*dep.
Public API surface
| Package | v0.0.13 | v0.0.14 |
|---|---|---|
@wifo/factory-core |
34 | 35 (+resolveContextDir) |
@wifo/factory-context |
18 | 18 |
@wifo/factory-harness |
~16 | ~16 |
@wifo/factory-runtime |
26 | 26 |
@wifo/factory-spec-review |
10 | 10 |
@wifo/factory-twin |
~7 | ~7 |
Reconciliations worth knowing
- 8 specs across 2 run-sequence invocations. Run-1 hit the same stale-dist issue as v0.0.12 (the runtime process loaded harness/dist at startup with the OLD apostrophe-strip code; spec A's fix landed mid-run but the running process never reloaded). Spec D phantom-no-converged on its own apostrophe-bearing test name; sequence stopped. Recovery: rebuild dist, fresh runtime process, re-run on remaining 5 specs (4/5 converged; spec H — claude-no-hooks — agent-exit-nonzero with work landed, same v0.0.11 worktree-sandbox friction shape). All 8 specs' work is correct; 232 core / 71 harness / 255 runtime / 106 spec-review tests pass.
--context-dirchange is breaking. Users with scripts hard-coded to./contextneed to pass--context-dir ./contextexplicitly, OR rename their dir to./.factory. The universal default is the dirfactory initcreates.createRequiredropped from core/cli.ts. v0.0.12's workaround → v0.0.13's persistence → v0.0.14's removal. The subprocess path eliminates the CJS/ESM friction entirely.- bun stays as the test runner. Every package's test script is
bun test src— unchanged. v0.0.13's bun-as-test-only documentation continues to apply. - v0.0.14 explicitly does NOT ship: the factory-cli umbrella package (deferred to v1.0.0); custom hook authoring (out of scope;
--setting-sourcesis enough); a--legacy-context-dir-defaultflag (rejected — the migration is one flag added per invocation);claude --no-hooks(the actual flag that exists is--setting-sources; we use it).