mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-09 03:20:56 +00:00
424 lines
24 KiB
Markdown
424 lines
24 KiB
Markdown
# Chisel calibration validation 1
|
||
|
||
Revision: `6bb6b5efcc0e36b52e3c097f532d9f2c00914c6c`
|
||
|
||
This is an independent reading of only the requested assignments. Scores use the
|
||
mapped purposes and the final calibration rubric. Boundary evidence is included
|
||
where it establishes whether a scoped mechanism is on a production common path.
|
||
|
||
## Summary
|
||
|
||
| Component | Lens | Score | Evidence confidence |
|
||
|---|---|---:|---|
|
||
| `fabro-workflow` | `ownership-boundaries` | 2 | High |
|
||
| `fabro-workflow` | `domain-model` | 2 | High |
|
||
| `fabro-http` | `domain-model` | 4 | High |
|
||
| `fabro-http` | `duplication-knowledge` | 4 | High |
|
||
| `fabro-web-app` | `ownership-boundaries` | 4 | Medium |
|
||
| `repository-ci` | `ownership-boundaries` | 4 | Medium |
|
||
| `repository-ci` | `domain-model` | 2 | High |
|
||
| `fabro-checkpoint` | `ownership-boundaries` | 2 | High |
|
||
| `fabro-checkpoint` | `simplicity` | 4 | Medium |
|
||
| `fabro-checkpoint` | `domain-model` | 2 | High |
|
||
| `fabro-checkpoint` | `duplication-knowledge` | 3 | Medium |
|
||
|
||
## `fabro-workflow`
|
||
|
||
### `ownership-boundaries`: 2
|
||
|
||
- **Evidence:** `lifecycle/mod.rs:53-80` presents `WorkflowLifecycle` as the
|
||
callback owner, and `lifecycle/git.rs:77-93, 397-401` gives `GitLifecycle`
|
||
its own `last_git_sha` state. The normal `RunSession::run` path nevertheless
|
||
creates a second `last_git_sha`, reconstructs it by listening to emitted
|
||
checkpoint, terminal, and Git events, then passes it back into finalization
|
||
(`operations/start.rs:821-856, 914-923`). Terminal responsibility is split
|
||
again: engine outcomes become terminal events in
|
||
`pipeline/finalize.rs:524-596`, while bootstrap, initialization, and
|
||
finalization errors become `run.failed` through the outer operation in
|
||
`operations/start.rs:176-285, 288-346`. These crossings occur on the normal
|
||
run and error paths, not at an optional edge.
|
||
- **Strongest counterevidence:** `operations/start.rs:796-953` is a recognizable
|
||
top-level owner for the initialize → execute → finalize → pull-request
|
||
sequence, and `WorkflowLifecycle` explicitly orders focused delegates for
|
||
each executor callback (`lifecycle/mod.rs:221-469`).
|
||
- **Why adjacent scores do not fit:** 3 does not fit because the caller always
|
||
mirrors and resupplies Git identity on the common run path, and terminal
|
||
failure handling routinely selects between two owners. 1 does not fit because
|
||
both the executor callback owner and the outer run-session owner are stable
|
||
and traceable; the problem is their competition, not the absence of owners.
|
||
- **Rule discrimination:** Decision rule 2 is decisive for the mirrored
|
||
`last_git_sha`. The phrase “complete lifecycle” is otherwise ambiguous about
|
||
whether an executor lifecycle may end before durability finalization; the
|
||
explicit state round-trip makes the result 2 without relying on that
|
||
ambiguity.
|
||
|
||
### `domain-model`: 2
|
||
|
||
- **Evidence:** The internal durable event shape stores
|
||
`Event::StageCompleted.status` as `String`
|
||
(`event/events.rs:264-272`). Both synthetic terminal-stage completion and
|
||
ordinary successful stage completion stringify the canonical
|
||
`StageOutcome` (`lifecycle/event.rs:215-240, 355-366`), after which the
|
||
mandatory event conversion reparses it and converts an unknown value to
|
||
`Failed` (`event/convert.rs:14-24, 309-333`). This typed → string → typed path
|
||
is part of every successful stage-completion event.
|
||
- **Strongest counterevidence:** `fabro_types::StageOutcome` is a stable
|
||
canonical type, most event fields are typed, and the fallback prevents an
|
||
unrecognized string from escaping into the stored projection.
|
||
- **Why adjacent scores do not fit:** 3 does not fit because common production
|
||
completion events depend on the invalid intermediate rather than using it as
|
||
a compatibility edge. 1 does not fit because the canonical status meaning is
|
||
clear and the conversion point is explicit.
|
||
- **Rule discrimination:** Decision rule 4 and the rubric's repository example
|
||
make this assignment unambiguous.
|
||
|
||
## `fabro-http`
|
||
|
||
### `domain-model`: 4
|
||
|
||
- **Evidence:** `ProxyPolicy` is a closed `System | Disabled` vocabulary;
|
||
parsing rejects every other boundary value
|
||
(`src/lib.rs:23-35`). Resolution gives explicit configuration precedence over
|
||
the environment, defaults absence to `System`, and rejects non-Unicode input
|
||
(`src/lib.rs:38-60`). Every async and blocking builder reaches that resolver
|
||
before construction (`src/lib.rs:160-166, 172-193`), while the deterministic
|
||
test helpers select the typed `Disabled` value
|
||
(`src/lib.rs:195-213`).
|
||
- **Strongest counterevidence:** The builder also exposes raw `no_proxy()` and
|
||
`proxy()` operations (`src/lib.rs:96-106`), so callers can combine an
|
||
underlying reqwest choice with `ProxyPolicy`; Unix-socket production callers
|
||
do use `no_proxy()` (`lib/foundation/fabro-client/src/client.rs:2123-2134`).
|
||
- **Why adjacent scores do not fit:** 3 does not fit because the common
|
||
policy-controlled constructors never interpret an invalid policy: they
|
||
return `HttpClientBuildError`. The raw builder operations represent valid
|
||
per-client transport configuration, not a second string vocabulary. 2 and 1
|
||
do not fit because no common-path conversion or unstable meaning is present.
|
||
- **Rule discrimination:** Decision rule 4 is potentially non-discriminating
|
||
if every forwarded low-level builder method is called an “escape hatch.”
|
||
Here `no_proxy()` carries no invalid intermediate and does not weaken
|
||
`ProxyPolicy::resolve`, so treating it as ordinary typed builder
|
||
configuration preserves the rule's distinction.
|
||
|
||
### `duplication-knowledge`: 4
|
||
|
||
- **Evidence:** `define_builder!` holds the complete shared async/blocking
|
||
builder policy once, including proxy resolution and construction
|
||
(`src/lib.rs:72-170`), and is instantiated for the two reqwest client kinds
|
||
(`src/lib.rs:172-193`). The four convenience constructors delegate to those
|
||
builders rather than reproducing policy (`src/lib.rs:195-213`).
|
||
- **Strongest counterevidence:** The generated facade necessarily lists each
|
||
forwarded reqwest method, and the test and non-test convenience constructors
|
||
have similar bodies.
|
||
- **Why adjacent scores do not fit:** 3 does not fit because the similar
|
||
forwarding and wrappers are syntax over one policy authority, not separately
|
||
maintained transport knowledge. 2 does not fit because a proxy-policy change
|
||
is made once in the macro/resolver, not synchronized across async and
|
||
blocking implementations. 1 does not fit because the authority is explicit.
|
||
- **Rule discrimination:** The rubric's `define_builder!` example directly
|
||
distinguishes shared macro expansion from semantic duplication; no material
|
||
ambiguity remains.
|
||
|
||
## `fabro-web-app`
|
||
|
||
### `ownership-boundaries`: 4
|
||
|
||
- **Evidence:** `entry.tsx:17-49` owns browser startup, chooses the normal or
|
||
installation route graph once, and installs shared SWR runtime policy.
|
||
`router.tsx:97-184` owns normal route composition. Shared transport and error
|
||
handling live in `lib/api-client.ts:64-160, 213-310`; shared reads such as
|
||
`useRun` and `useRunState` live in `lib/queries.ts:182-193`; run mutations and
|
||
their cache lifecycle live in `lib/mutations.ts:65-132`; and run-scoped SSE
|
||
subscription, invalidation, resync, and cleanup live in
|
||
`lib/run-events.ts:129-309`. The representative busy route composes those
|
||
owners rather than reimplementing them
|
||
(`routes/run-detail.tsx:79-145, 313-379`).
|
||
- **Strongest counterevidence:** Some route-local CRUD actions call the shared
|
||
API facade directly, and `run-detail.tsx:193-205` coordinates delete state,
|
||
cache invalidation, toast, and navigation in the route.
|
||
- **Why adjacent scores do not fit:** 3 does not fit because the counterevidence
|
||
is local page UX ownership; it does not split a shared transport, read,
|
||
mutation, or subscription lifecycle. 2 does not fit because routine run-page
|
||
changes use the established owners rather than coordinating competing ones.
|
||
1 does not fit because startup, routing, transport, caching, and streaming
|
||
each have readily identifiable homes.
|
||
- **Rule discrimination:** “One owner” is mildly non-discriminating for a large
|
||
browser application unless responsibility is evaluated at lifecycle
|
||
granularity. Using the rubric's `apiData`/`useRun` example, route composition
|
||
is not itself a second owner. Confidence is Medium because this is the
|
||
largest sampled scope.
|
||
|
||
## `repository-ci`
|
||
|
||
### `ownership-boundaries`: 4
|
||
|
||
- **Evidence:** `rust.yml:3-40` owns Rust branch/PR/manual triggers and
|
||
concurrency, while its jobs contain format, lint, generated-doc, Linux test,
|
||
twin E2E, and manual macOS lifecycles (`rust.yml:48-147`).
|
||
`typescript.yml:3-34` owns the corresponding TypeScript triggers and
|
||
concurrency, and its jobs contain typecheck, test, and integrated SPA/Rust
|
||
build lifecycles (`typescript.yml:36-77`). Delegation to `cargo dev` is the
|
||
mapped dependency on build tooling, not reverse ownership.
|
||
- **Strongest counterevidence:** The TypeScript build invokes a Rust build
|
||
(`typescript.yml:75-77`), and invalid path selectors mean some intended
|
||
changes do not start the declared workflows.
|
||
- **Why adjacent scores do not fit:** 3 does not fit because the cross-language
|
||
build is the intentional embedded-SPA integration boundary, not friction, and
|
||
selector validity is classified under domain model by decision rule 6. 2
|
||
does not fit because no routine job requires coordination between competing
|
||
CI owners. 1 does not fit because the two language validation homes and their
|
||
dependency direction are explicit.
|
||
- **Rule discrimination:** The score-4 phrase “complete lifecycle” is
|
||
non-discriminating for hosted CI if it is read to require repository
|
||
ownership of GitHub's runner lifecycle. This score treats the checked-in
|
||
trigger/job lifecycle as the mapped responsibility and the platform as an
|
||
intended boundary.
|
||
|
||
### `domain-model`: 2
|
||
|
||
- **Evidence:** Both Rust trigger selectors name `openapi/**`
|
||
(`rust.yml:18,34`), but that revision has no tracked target there; the actual
|
||
API contract is `docs/public/api-reference/fabro-api.yaml`, which the
|
||
TypeScript client generation command consumes
|
||
(`lib/packages/fabro-api-client/package.json:7`). The real contract path is
|
||
absent from both workflow path filters. In addition, all three zizmor
|
||
`stale-action-refs` identifiers target `rust.yml:37`, `:49`, and `:62`
|
||
(`zizmor.yml:1-6`), which are respectively the end of trigger setup, the
|
||
`fmt` job key, and a `run` command—not action references at this revision.
|
||
These invalid identifiers sit directly in trigger and static-validation
|
||
configuration.
|
||
- **Strongest counterevidence:** The workflow/job vocabulary itself is stable,
|
||
all jobs and action pins have clear meanings, and changes under the large
|
||
valid Rust and TypeScript source selectors do trigger their expected suites.
|
||
- **Why adjacent scores do not fit:** 3 does not fit because the dead OpenAPI
|
||
selector is present in both routine branch and PR paths, while every scoped
|
||
zizmor exception lacks a current target. 1 does not fit because the overall
|
||
workflow and job model remains stable; the defect is a recurring set of
|
||
invalid identifiers.
|
||
- **Rule discrimination:** Decision rule 6 is decisive that these are domain
|
||
pressure rather than ownership or duplication. It does not state when one or
|
||
more dead selectors move from 3 to 2; centrality in both trigger modes and
|
||
total staleness of the scoped zizmor selectors supply that discrimination
|
||
here.
|
||
|
||
## Control: `fabro-checkpoint`
|
||
|
||
### `ownership-boundaries`: 2
|
||
|
||
- **Evidence:** The mapped component claims metadata branches, but its
|
||
production boundary consumer owns the metadata writer's branch, parent OID,
|
||
discovery, remote, and push lifecycle
|
||
(`fabro-workflow/src/run_metadata.rs:272-282, 313-439`). On every snapshot,
|
||
that caller validates entries, individually drives `Store` through blobs,
|
||
tree, commit, and ref update, and retains the parent identity for the next
|
||
write (`run_metadata.rs:313-350`). `BranchStore` provides a contained
|
||
read-modify-write owner (`branch.rs:17-24, 42-81`) but has no production
|
||
caller at this revision.
|
||
- **Strongest counterevidence:** The dependency direction is intended
|
||
(`fabro-workflow` depends on `fabro-checkpoint`), and the low-level `Store`
|
||
consistently owns Git object/ref operations (`git.rs:101-227`).
|
||
- **Why adjacent scores do not fit:** 3 does not fit because the lifecycle
|
||
crossing occurs on every metadata snapshot, not in an isolated adapter. 1
|
||
does not fit because low-level Git ownership and the caller's higher-level
|
||
writer ownership are both stable; the problem is the split between them.
|
||
- **Rule discrimination:** Decision rule 2 applies because the caller retains
|
||
and resupplies branch/parent identity to complete successive writes. The
|
||
rubric does not say whether a deliberately low-level `Store` narrows the
|
||
mapped ownership claim; the explicit mapped claim to metadata branches makes
|
||
this crossing discriminating.
|
||
|
||
### `simplicity`: 4
|
||
|
||
- **Evidence:** The production `Store` has direct blob, tree, commit, and ref
|
||
operations (`git.rs:123-226`). Tree conversion is a single read recursion and
|
||
a single bottom-up write path (`git.rs:229-310`). At the higher level,
|
||
`BranchStore::write_with` is a linear resolve → read → mutate → write → commit
|
||
→ update sequence (`branch.rs:56-81`), and entry operations are small
|
||
delegates (`branch.rs:84-117`). Necessary Git layering is visible rather than
|
||
hidden behind competing configuration machinery.
|
||
- **Strongest counterevidence:** There are two entry levels, and the production
|
||
metadata writer uses the lower-level `Store` instead of `BranchStore`.
|
||
- **Why adjacent scores do not fit:** 3 does not fit because choosing the
|
||
low-level entry is required for replace-whole-tree and remote-parent behavior,
|
||
not unnecessary indirection. 2 does not fit because the scoped common
|
||
operations do not navigate competing implementations or configuration. 1
|
||
does not fit because both paths are directly traceable.
|
||
- **Rule discrimination:** Ownership rule 2 could otherwise cause the
|
||
out-of-scope metadata writer's machinery to be counted again as simplicity
|
||
friction. The lens exclusions make that non-discriminating evidence here;
|
||
within the scoped implementation, the production primitives are direct.
|
||
|
||
### `domain-model`: 2
|
||
|
||
- **Evidence:** `TreeEntries::set` accepts any `String` path without validation
|
||
(`git.rs:46-60`), and `write_tree` later interprets it by splitting on `/`
|
||
(`git.rs:149-153, 270-293`). The common metadata caller must therefore define
|
||
and apply `validate_metadata_path` outside this component before every
|
||
`TreeEntries` construction
|
||
(`fabro-workflow/src/run_metadata.rs:313-332, 471-480`). The component also
|
||
maps every unrecognized Git file mode to `Blob`
|
||
(`git.rs:21-35, 229-250`) rather than rejecting an unsupported state.
|
||
- **Strongest counterevidence:** `FileMode` is otherwise a closed enum, Git
|
||
object IDs use `git2::Oid`, and the current production metadata caller does
|
||
reject empty, absolute, dot-segment, and empty-segment paths before writing.
|
||
- **Why adjacent scores do not fit:** 3 does not fit because external path
|
||
validation is mandatory on every common metadata snapshot and the canonical
|
||
`TreeEntries` shape can always hold an invalid path. 1 does not fit because
|
||
the intended path and mode meanings remain clear and production does have a
|
||
validation step.
|
||
- **Rule discrimination:** Decision rule 4 clearly places the caller-validated
|
||
`TreeEntries` intermediate at 2. Whether unknown Git modes are a compatibility
|
||
escape hatch is ambiguous by itself, but it is not needed to choose the
|
||
score.
|
||
|
||
### `duplication-knowledge`: 3
|
||
|
||
- **Evidence:** Branch-to-full-ref formatting is repeated in `Store::update_ref`,
|
||
`resolve_ref`, and `delete_ref` (`git.rs:182-225`), and the boundary metadata
|
||
writer has another `full_ref` transformation
|
||
(`fabro-workflow/src/run_metadata.rs:364-439`). `BranchStore::read_entry`,
|
||
`read_entries`, `list_entries`, and `tip_tree` also repeat parts of branch-tip
|
||
resolution (`branch.rs:119-184`). These repetitions are local and stable, but
|
||
there is no single helper enforcing them.
|
||
- **Strongest counterevidence:** Mutation sequencing is authoritative in
|
||
`BranchStore::write_with` (`branch.rs:56-81`), metadata branch naming has one
|
||
`META_BRANCH_PREFIX` constant (`lib.rs:7`), Git-author defaults have one
|
||
`Default` implementation (`author.rs:13-20`), and the repeated ref syntax is a
|
||
fixed Git protocol form rather than frequently changing Fabro policy.
|
||
- **Why adjacent scores do not fit:** 4 does not fit because ref normalization
|
||
and branch-tip traversal are still represented in several places. 2 does not
|
||
fit because there is no direct evidence that a routine checkpoint change
|
||
must alter those stable protocol transformations in sync; the repetitions are
|
||
isolated implementation knowledge. 1 does not fit because each policy has an
|
||
identifiable local authority even where a helper is absent.
|
||
- **Rule discrimination:** Decision rule 5 leaves a real 3-versus-4 ambiguity:
|
||
repeated `refs/heads/` can be classified as harmless protocol syntax. I score
|
||
3 because the same branch-to-ref transformation crosses the component
|
||
boundary, but do not score 2 without evidence of routine synchronization.
|
||
|
||
## Overall rubric observations
|
||
|
||
- Decision rule 2 successfully distinguishes focused delegates from a lifecycle
|
||
that sends identity back through an event/caller round trip.
|
||
- Decision rule 6 prevents dead CI selectors from being double-counted as
|
||
ownership defects, but needs centrality/recurrence evidence to distinguish 2
|
||
from 3.
|
||
- “One owner” and “complete lifecycle” need responsibility-sized interpretation
|
||
for route trees and hosted CI; otherwise healthy composition cannot reach 4.
|
||
- Decision rule 5 correctly keeps stable protocol repetition from automatically
|
||
becoming score 2, but the line between harmless syntax and a repeated
|
||
transformation remains the least discriminating part of this sample.
|
||
|
||
## Round 2 revalidation
|
||
|
||
| Component | Lens | Score | Confidence |
|
||
|---|---|---:|---|
|
||
| `fabro-http` | `duplication-knowledge` | 3 | Medium |
|
||
| `repository-ci` | `ownership-boundaries` | 2 | High |
|
||
| `fabro-checkpoint` | `ownership-boundaries` | 2 | High |
|
||
| `fabro-checkpoint` | `simplicity` | 3 | High |
|
||
| `fabro-checkpoint` | `domain-model` | 2 | High |
|
||
| `fabro-checkpoint` | `duplication-knowledge` | 3 | Medium |
|
||
|
||
### `fabro-http` × `duplication-knowledge`: 3
|
||
|
||
- **Decisive evidence:** Proxy disabling has two concrete semantic
|
||
representations in the mapped entry layer: callers may set
|
||
`ProxyPolicy::Disabled` (`src/lib.rs:23-27, 90-94`), or call the separately
|
||
exposed `no_proxy()` builder operation (`src/lib.rs:96-100`). The former is
|
||
interpreted by calling the same underlying `inner.no_proxy()` transformation
|
||
during `build` (`src/lib.rs:160-165`). Both forms are used on direct boundary
|
||
paths: test constructors select the enum (`src/lib.rs:199-213`), while the
|
||
Unix-socket transport selects `no_proxy()`
|
||
(`lib/foundation/fabro-client/src/client.rs:2123-2134`).
|
||
- **Adjacent scores:** 4 does not fit revised rule 6 because there is a concrete
|
||
second representation of the same no-proxy decision. 2 does not fit because
|
||
an ordinary proxy-policy extension does not require manually synchronizing
|
||
those call sites; async and blocking policy construction still share the one
|
||
`define_builder!` mechanism (`src/lib.rs:72-193`). 1 does not fit because the
|
||
resolver remains a stable authority.
|
||
- **Remaining ambiguity:** `no_proxy()` can reasonably be viewed as a lower-level
|
||
reqwest operation rather than a second Fabro policy. Revised rule 6 makes 3
|
||
the conservative result because `ProxyPolicy::Disabled` is implemented by
|
||
that exact operation, but this classification keeps confidence at Medium.
|
||
|
||
### `repository-ci` × `ownership-boundaries`: 2
|
||
|
||
- **Decisive evidence:** The Rust check explicitly scans
|
||
`docs/public/api-reference/fabro-api.yaml` in its legacy-identity guard
|
||
(`rust.yml:80-92`), but neither push nor pull-request triggers include that
|
||
real path (`rust.yml:3-35`); they include the nonexistent `openapi/**`
|
||
selector instead (`rust.yml:18,34`). A routine API-contract change can
|
||
therefore change a scanned target without starting its owning check.
|
||
- **Adjacent scores:** 3 does not fit because the non-triggering target is on a
|
||
routine branch/PR check path, not an isolated manual edge. 1 does not fit
|
||
because the workflow, jobs, and intended trigger owner remain identifiable.
|
||
4 is directly excluded by revised rule 3's trigger-coverage requirement.
|
||
- **Remaining ambiguity:** `typescript.yml:76` also invokes a Rust build from a
|
||
narrower trigger set, but that broader interpretation is unnecessary; the
|
||
explicitly scanned, non-triggering API contract is sufficient for 2.
|
||
|
||
### `fabro-checkpoint` × `ownership-boundaries`: 2
|
||
|
||
- **Decisive evidence:** The mapped owner exposes low-level `Store` primitives,
|
||
while the routine metadata caller reconstructs the mapped branch lifecycle:
|
||
`RunMetadataWriter` owns branch, parent, and discovery state
|
||
(`fabro-workflow/src/run_metadata.rs:272-282`), then validates entries and
|
||
sequences blob, tree, commit, ref update, and retained parent state on every
|
||
snapshot (`run_metadata.rs:313-350`). No production boundary uses the
|
||
component's higher-level `BranchStore`.
|
||
- **Adjacent scores:** 3 does not fit because every metadata snapshot traverses
|
||
the split. 1 does not fit because the low-level Git owner and caller-side
|
||
lifecycle are both stable. 4 is directly excluded by revised rule 2: the
|
||
routine caller reconstructs a lifecycle the map assigns to this component.
|
||
- **Remaining ambiguity:** A narrower map that assigned only Git object
|
||
primitives to `fabro-checkpoint` could make this healthy delegation, but the
|
||
actual map explicitly assigns metadata branches and checkpoint commits.
|
||
|
||
### `fabro-checkpoint` × `simplicity`: 3
|
||
|
||
- **Decisive evidence:** `Cargo.toml:16-24` carries `fabro-store` as a production
|
||
dependency, but scoped production code does not use it. The component also
|
||
exposes `BranchStore` as a parallel entry layer (`branch.rs:17-24`) that has
|
||
no production caller at this revision; the common metadata path uses `Store`
|
||
directly. The active `Store` path itself remains linear and direct
|
||
(`git.rs:123-226`).
|
||
- **Adjacent scores:** 4 is explicitly capped at 3 by revised rule 4 for the
|
||
unused production dependency and parallel unused entry layer. 2 does not fit
|
||
because routine production work does not repeatedly navigate those unused
|
||
elements; its `Store` path is direct. 1 does not fit because a stable common
|
||
path is easy to trace.
|
||
- **Remaining ambiguity:** Either isolated fact independently supplies the
|
||
revised rule's cap, so there is no material score ambiguity.
|
||
|
||
### `fabro-checkpoint` × `domain-model`: 2
|
||
|
||
- **Decisive evidence:** `TreeEntries::set` accepts arbitrary string paths
|
||
(`git.rs:46-60`) before `write_tree` interprets them structurally
|
||
(`git.rs:149-153, 270-293`). Every common metadata snapshot must validate
|
||
those paths outside the mapped entry before constructing `TreeEntries`
|
||
(`fabro-workflow/src/run_metadata.rs:313-332, 471-480`).
|
||
- **Adjacent scores:** 3 does not fit revised rule 5 because caller validation
|
||
does not isolate an invalid-capable mapped entry used on every snapshot. 1
|
||
does not fit because path meaning is stable and the caller does enforce it.
|
||
4 is excluded because the canonical entry type itself admits invalid states.
|
||
- **Remaining ambiguity:** Unknown Git modes also collapse to `Blob`
|
||
(`git.rs:21-35`), but that compatibility question is not needed for the
|
||
score; the routine path shape is decisive.
|
||
|
||
### `fabro-checkpoint` × `duplication-knowledge`: 3
|
||
|
||
- **Decisive evidence:** The short branch name is converted to
|
||
`refs/heads/{branch}` independently in `Store::update_ref`, `resolve_ref`, and
|
||
`delete_ref` (`git.rs:182-225`), while the routine boundary writer carries a
|
||
second `full_ref` conversion
|
||
(`fabro-workflow/src/run_metadata.rs:364-439`). These are concrete repeated
|
||
representations, but of stable Git protocol knowledge.
|
||
- **Adjacent scores:** 4 does not fit revised rule 6 because the
|
||
branch-to-full-ref transformation has a concrete second representation. 2
|
||
does not fit because no ordinary mapped change is shown to require
|
||
synchronizing the stable Git namespace transformations; repeated call sites
|
||
alone are insufficient. 1 does not fit because the transformation and its
|
||
local authorities are clear.
|
||
- **Remaining ambiguity:** The literal can also be classified as harmless Git
|
||
syntax, which the lens excludes. Its repetition across the mapped boundary
|
||
supports 3, but the harmless-syntax distinction keeps confidence at Medium.
|