You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
refactor(models): enforce canonical routing names and target authority #391
Enforce strict canonical model-routing names and explicit global/project config and profile target authority as the canonical-boundary slice of the #382 chain. This unit adds the canonical boundary and alias denial without changing durable writes or profile materialization.
main
└─ #389 shared types/normalization, sync/async authority, compatibility wrappers
└─ #396 fail-closed apply and profile safety
└─ #395 direct export contract/tests plus UI/lifecycle consumers
└─ 📍 #391 strict canonical names and target authority
└─ durable write
└─ materialization
Scope and acceptance criteria
Add one shared canonical name validator and use it from saved-config validation and both sync and async metadata discovery.
Metadata parsing may trim surrounding YAML whitespace before validation. Saved-config top-level keys have a stricter boundary: each raw key must already equal its canonical trimmed form. A padded saved-config key is invalid and must not be silently canonicalized.
Preserve valid unknown names, including internal spaces and supported punctuation. Reject quote characters and C0/C1 control characters at both metadata and saved-config boundaries. C0/C1 rejection includes the control ranges already covered by the package contract, including U+0000 through U+001F and U+007F through U+009F.
Validate the complete saved document. Any invalid top-level name or routing entry invalidates the document with its path; valid siblings must not be silently retained while invalid data is discarded.
Preserve the existing value contract: JSON null is invalid, the string inherit remains a literal model id, and omission or an empty routing entry retains its current omission semantics.
Add one explicit resolver for global and project model-config and profile paths. Deny normalized aliases before any read, apply, or write operation, with platform-appropriate comparison, including the cwd == home case and Windows case behavior.
Add only the minimal save-validation bridge needed to delegate to the shared normalizer. Do not redesign the write algorithm.
Keep the real saved-config, metadata discovery, and sync and async production paths on the shared authority. Do not add a test-only runtime seam or a fake production caller.
Add differential tests proving a padded saved key fails closed before apply or write, existing profile bytes remain unchanged, unrelated profile fields remain byte-identical, and alias denial happens before any config or profile write.
Explicitly create every test fixture directory before writing fixtures, including the root/agents directory used by metadata-discovery tests.
Intentionally out of scope
No durable saved-config write algorithm, temporary-file policy, cleanup, rename outcome, or directory durability.
No profile materialization, frontmatter rewrite, inherit-by-omission, profile-field deletion, or unrelated-field preservation change beyond the required differential proof that this unit does not change those behaviors.
No automatic branch rewrite, commit, push, PR publication, merge, or approval of any later slice.
Strict TDD and verification
Strict TDD is required. The RED phase must be an executable assertion-level failure after dependency hydration, not a missing-module or environment error. The current governance snapshot records assertion-level RED and focused GREEN 4/4 for the combined candidate only. It is not issue completion evidence for this canonical-boundary slice. The implementation PR must report its own observed evidence before claiming completion.
Environment prerequisite: use an authorized implementation environment with dependencies hydrated by the repository package manager, pnpm@11.1.1, including @earendil-works/pi-tui.
Run the focused and package-supported commands after the test file exists:
node --experimental-strip-types --test tests/model-routing-authority.test.ts
node --experimental-strip-types --test tests/gentle-ai.test.ts
pnpm test
RED: add the smallest assertions for padded saved keys, complete-document rejection, sync and async canonical-name parity, alias denial before writes, and null and inherit preservation. Record the observed assertion failures after dependencies are available.
GREEN: implement only the shared validator, strict document boundary, explicit target resolver, and minimal save-validation bridge. Record the focused test result.
TRIANGULATE: exercise sync and async discovery, valid unknown internal spaces and punctuation, quote and C0/C1 rejection, global and project selection, cwd == home, Windows path comparison behavior, invalid-source preservation, explicit fixture-directory creation, and the real /gentle:models read and apply path.
REFACTOR: remove superseded duplicate validation without changing behavior, then rerun the focused commands and check-only syntax validation.
Review workload forecast and hard stop
Forecast only, measured against the clean immediate main base after #389, #396, and #395 merge: 230 lower / 300 realistic / 370 upper A+D. The hard stop is above 400 A+D. No size:exception is permitted. If the clean diff exceeds 400, stop before publication and return to design for another causal split. Do not line-golf the diff or remove the tests that establish the safety boundary.
Outcome
Enforce strict canonical model-routing names and explicit global/project config and profile target authority as the canonical-boundary slice of the #382 chain. This unit adds the canonical boundary and alias denial without changing durable writes or profile materialization.
Chain context
mainfirst. refactor(models): isolate saved-routing read authority #389 delivers shared types and normalization, synchronous and asynchronous read authority, and minimal compatibility wrappers used by existing production callers. refactor(models): isolate fail-closed saved-routing apply #396 delivers fail-closed apply behavior and profile byte, modification-time, and no-write safety. refactor(models): migrate routing UI and lifecycle consumers #395 delivers the direct export migration and export contract tests together with the UI and lifecycle consumer migration. This unit must be based on that resultingmain, not on the stopped candidate or PR refactor(models): centralize routing persistence authority #385 evidence.main. Use stacked-to-main delivery. The PR diff must contain only this strict target-authority unit.enhancement,status:approved.mainand the resulting base is revalidated. Approval grants no authority to commit, push, publish a PR, merge, or approve a later slice.Dependency diagram:
Scope and acceptance criteria
U+0000throughU+001FandU+007FthroughU+009F.nullis invalid, the stringinheritremains a literal model id, and omission or an empty routing entry retains its current omission semantics.cwd == homecase and Windows case behavior.root/agentsdirectory used by metadata-discovery tests.Intentionally out of scope
Strict TDD and verification
Strict TDD is required. The RED phase must be an executable assertion-level failure after dependency hydration, not a missing-module or environment error. The current governance snapshot records assertion-level RED and focused GREEN 4/4 for the combined candidate only. It is not issue completion evidence for this canonical-boundary slice. The implementation PR must report its own observed evidence before claiming completion.
Environment prerequisite: use an authorized implementation environment with dependencies hydrated by the repository package manager,
pnpm@11.1.1, including@earendil-works/pi-tui.Run the focused and package-supported commands after the test file exists:
inheritpreservation. Record the observed assertion failures after dependencies are available.cwd == home, Windows path comparison behavior, invalid-source preservation, explicit fixture-directory creation, and the real/gentle:modelsread and apply path.Review workload forecast and hard stop
Forecast only, measured against the clean immediate
mainbase after #389, #396, and #395 merge: 230 lower / 300 realistic / 370 upper A+D. The hard stop is above 400 A+D. Nosize:exceptionis permitted. If the clean diff exceeds 400, stop before publication and return to design for another causal split. Do not line-golf the diff or remove the tests that establish the safety boundary.