T-QGMQ-vault-facilities-decision
State: planning/proposed · Impact: medium · Complexity: small
Agent Manager wants the vault to know it is a git repository and offer a validated commit, and polish already wants its own pre-commit schema check — two experiences wanting the same kind of platform facility, with no settled answer for where that logic lives or how two experiences contribute one commit-time check without either owning it. This task decides the placement once, so the Stage 5 git tasks build against a named shape instead of inventing their own.
| Location | Role today |
|---|---|
solutions/determined/crates/determined-core/src/ctx.rs#Ctx | Bundles vault_dir, repo_root, state_dir, http, jobs, engine, vaults per call; jobs/engine are the only boot-injected Arc<dyn Trait> fields today, added explicitly rather than through a service locator |
solutions/determined/crates/determined-platform/src/backends/mod.rs#polish | The port-trait pattern: 17 capabilities dispatch through a macro-generated ports::XPort accessor gated by Capability/Backend (Node/Shadow/Rust); polish, vault_notes, media, ocr are hand-written exceptions because they were never TypeScript apps with a node arm to migrate from |
solutions/determined/crates/determined-platform/src/openers.rs | The “no port trait, on purpose” pattern: platform-state surface (which apps are installed, how to launch one) constructed inline as SystemOpenerCatalog, deliberately outside Capability/Backend because it is registry state, not domain logic with two implementations |
solutions/determined/crates/determined-platform/src/api/v1/agent_skills.rs | A third, already-shipped pattern: determined_agent_skills::get_notes(&state.ctx(), ...) and its siblings are called directly, with no port trait and no Capability variant — the crate was Rust-native from the start, so there is nothing to migrate |
solutions/determined/docs/planning/rust-backends/ARCHITECTURE.md | Documents the port/Capability/Backend migration scaffolding for the 19 experiences that had a Node sidecar; its “why it has no port” table counts 12 endpoints (settings.rs, jobs.rs, vault.rs, health.rs) but omits agent_skills.rs, which also has no port for the same reason |
packages/rust/dir-aspect/src/lib.rs#Aspect | Detects Git/Obsidian/Generic from marker directories; mirrored on the wire as determined-core/src/wire/mod.rs#VaultAspect. Nothing gates a facility on it yet |
solutions/determined/crates/determined-platform/src/settings.rs#SettingsStore | Per-namespace JSON store (get_namespace/set_namespace), the existing precedent for a per-vault “always ignore this rule” setting |
solutions/determined/crates/determined-agent-skills/src/lib.rs#get_component_tree | The only check today that would refuse a commit — a discover-refusal — reachable only through the component-tree scan, not through any commit path |
solutions/determined/experiences/polish/app/components/PolishSchemaValidationPane.vue | Polish’s own frontmatter schema-validation pass (determined-platform/src/api/v1/polish_schema.rs), a second pre-commit-check candidate with no shared home either |
solutions/determined/docs/decisions/ | A second, solution-local decision catalog (incrementing ids, D-0001–D-0024+) distinct from the root docs/planning/decisions/; this question spans agent-pants, determined and determined-platform, so it belongs in the root corpus per D-0002-entity-identifier-shape, not here |
Proposed
Section titled “Proposed”One decision, in docs/planning/decisions/, that:
- Places the git status/commit logic as a plain domain crate (mirroring
determined-agent-skills) called directly from a newdetermined-platform/src/api/v1/git.rsviaCtx— noports::GitPorttrait, noCapability::Gitvariant, noBackendtoggle. Git has no Node predecessor to migrate from, so the port/Capabilitymachinery (built for that migration) does not apply; it is domain logic overeasy-git/fluent-git, not bare registry state, soopeners.rs’s carve-out does not fit either. - Names a pluggable pre-commit validator mechanism: a new trait,
Ctxextended with a boot-injectedVec<Arc<dyn ...>>following the same shape asJobSink/EngineProbe, so agent-pants registers its discover-refusal check (promoted out ofget_component_treeinto a standalonecheck_vault) and polish registers its schema validation, with neither owning the hook. - Names the ignore-store: a
SettingsStorenamespace, keyed per rule id and scoped per vault, holding “always ignore” choices; “ignore once” is UI/session state, never persisted. - States that this decision, not
solutions/determined/docs/decisions/, is the home for the question, because it spans agent-pants, determined and determined-platform.
Approach
Section titled “Approach”- Compare the three integration shapes already in the codebase against git’s needs: the
port/
Capability/Backendpattern (built for Node-to-Rust migration — does not apply, git has no Node arm), theopeners.rs“no port trait, on purpose” pattern (platform registry state — does not fit, git status is domain logic over a git primitive crate), and theagent_skills.rsdirect-call pattern (a Rust-native domain crate with no migration story — the closest fit). Record the choice and the reasoning. - Design the pluggable-validator trait’s shape (method signature, what it returns — a list of
warnings the caller can ignore vs. refusals it cannot) and where
Ctxcarries the registered list, extending theJobSink/EngineProbeboot-injection precedent. Open decision: whether registration happens inAppState::newdirectly or through a small builder, given the number of experiences expected to register (two, to start). - Name the settings namespace and per-rule key shape for the ignore-store.
- Mint the id:
takenIdChars('docs/planning/decisions', 'D')thenmintBase36(<slug-seed>, taken)(solutions/ontological/lib/model/identifier.ts); hand-write frontmatter matchingdocs/planning/decisions/D-0002-entity-identifier-shape.md’s shape (seeD-A27T’s frontmatter for a worked example) —sdlcships nodecisionnoun, so this is the one entity type authored by hand rather than through acreateverb. - Write the decision body (Context, Decision, Why, Options considered — the three shapes from step 1 — Consequences, Migration, Open questions), citing the Today-table locations as evidence.
- Correct
solutions/determined/docs/planning/rust-backends/ARCHITECTURE.md’s “why it has no port” table: it counts 12 port-free endpoints but omitsagent_skills.rs; add it and note the newgit.rswill join it for the same reason. - Verify:
sdlc entities validate docs/planning/decisions/D-<id>-*.md.
| Area | Note |
|---|---|
docs/planning/decisions/D-<id>-vault-facility-placement.md | The decision: placement, the validator trait, the ignore-store |
solutions/determined/docs/planning/rust-backends/ARCHITECTURE.md | Add agent_skills.rs (and the coming git.rs) to the “why it has no port” table |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
docs/planning/decisions/D-<id>-vault-facility-placement.mdexists, its id was minted withmintBase36againsttakenIdChars('docs/planning/decisions', 'D')(no collision with an existing decision id), andsdlc entities validatepasses on it. - AC-2: The decision names the git facility’s crate/module and its
Ctx/api/v1shape, and states explicitly that it carries noports::GitPorttrait and noCapability::Gitvariant, with the reasoning against both the port/Capabilitypattern and theopeners.rspattern. - AC-3: The decision names the pre-commit validator trait, its
Ctxregistration point, and lists agent-pants’ discover-refusal check and polish’s schema validation as its first two registrants. - AC-4: The decision names the ignore-store’s settings namespace and its per-rule key shape.
- AC-5:
solutions/determined/docs/planning/rust-backends/ARCHITECTURE.md’s “why it has no port” table listsagent_skills.rsalongside the existing four rows.
Out of scope
Section titled “Out of scope”- Implementing the git status service, the commit op, or either validator — T-NS6Z-vault-git-status-service and T-L7YP-git-panel-commit-with-validation build against this decision, they are not decided here.
- Amending
solutions/determined/docs/decisions/(the solution-local catalog) — this question is cross-solution and lives in the root corpus instead. - Choosing the shell composable’s exact name or file — a one-line consequence of this decision, finalized by the task that writes it.
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”From the user’s Agent Manager planning request of 2026-09-07: git awareness as a vault facility was
one of the eight brain-dump areas, and the same request separately wanted polish’s schema
validation pluggable at commit time — two experiences wanting one commit-time hook. Grounded in the
assessment solutions/agent-pants/docs/vault/design/workflow-and-ux-assessment.md. Member of
M-BMFD.