fabro/.chisel/calibration/work/validation-1.md
2026-07-27 11:58:54 -04:00

24 KiB
Raw Blame History

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.