mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-09 22:33:37 +00:00
25 KiB
25 KiB
Chisel calibration validation 3
Revision reviewed: 6bb6b5efcc0e36b52e3c097f532d9f2c00914c6c.
This is an independent reading of the final rubric and the assigned component
scopes. I traced representative production entry points and direct boundary
callers. I did not inspect prior calibration scores or any other file in
.chisel/calibration/work/.
Score 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 | 3 | Medium |
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 | 3 | Medium |
fabro-checkpoint |
domain-model | 2 | High |
fabro-checkpoint |
duplication-knowledge | 3 | Medium |
fabro-workflow
ownership-boundaries: 2 (High)
- Evidence:
src/lifecycle/mod.rs:53-80,221-469provides a real centralWorkflowLifecycleand explicitly orders focused event, hook, fidelity, Git, artifact, status, and circuit-breaker delegates. Its terminal callback, however, only forwardson_run_endto the hook. Normal terminal persistence, metadata completion, terminal event emission, and sandbox stopping instead live insrc/pipeline/finalize.rs:524-635. Bootstrap and execution failures take another terminal path insrc/operations/start.rs:176-345, whileRunSession::runalso installs cleanup and drain guards atsrc/operations/start.rs:889-947. A routine terminal-lifecycle change must therefore coordinate the lifecycle orchestrator, finalizer, and detached failure/guard paths. - Strongest counterevidence: The normal phase sequence is plainly owned by
RunSession::run(initialize -> execute -> finalize -> pull_request), and callback ordering inside graph execution has one obvious owner,WorkflowLifecycle. - Why 3 does not fit: Terminal completion, failure, persistence, and cleanup are common paths, not isolated edge compatibility. The split therefore remains central even though each individual phase is understandable.
- Why 1 does not fit: Stable phase owners and a stable dependency direction are readily identifiable; the problem is coordination among them, not the absence of ownership.
- Rule discrimination: The repository example correctly requires terminal inspection and rule 1 makes the common terminal split score-capping. Decision rule 2 is less literal here because no single identity is resupplied across every split, but the score does not depend on that rule.
domain-model: 2 (High)
- Evidence:
src/lifecycle/event.rs:319-390starts with the typedStageOutcomeon anOutcome, serializes it withoutcome.status.to_string(), and stores the result in theEvent::StageCompleted.status: Stringfield declared atsrc/event/events.rs:264-293. Every successful stage then passes throughsrc/event/convert.rs:14-24,309-348, which reparses the string and silently converts an unknown value into a non-retryable failure. This is the ordinary durable-event path, not an import-only compatibility path. - Strongest counterevidence: The destination event model already has the
canonical
fabro_types::StageOutcome, parallel-branch completion carries it directly, and other core run concepts use typed IDs, reasons, timings, and an opaqueResumeState(src/pipeline/types.rs:252-285). - Why 3 does not fit: The invalid intermediate occurs for each ordinary successful stage before durable interpretation, so it is central rather than an isolated escape hatch.
- Why 1 does not fit:
StageOutcomeitself has a stable, typed meaning; the defect is the recurring string round trip between two typed points. - Rule discrimination: Decision rule 4 is directly discriminating here: this is exactly a common-path invalid intermediate.
fabro-http
domain-model: 4 (High)
- Evidence:
src/lib.rs:23-61gives proxy behavior a closedProxyPolicy::{System, Disabled}vocabulary. The environment boundary accepts case-insensitive valid names, rejects every other value with a typedHttpClientBuildError, handles non-Unicode values explicitly, gives explicit policy precedence over the environment, and resolves absence toSystem. Both generated builders invoke this resolver before constructing a client (src/lib.rs:72-193), and the test-client entry points selectProxyPolicy::Disabledrather than passing an unchecked string (src/lib.rs:195-213). - Strongest counterevidence: The facade deliberately exposes reqwest's
lower-level
Proxyand.no_proxy()operations, so callers can compose transport details outside the two-value environment policy. - Why 3 does not fit: Those operations are typed builder choices, not
unvalidated representations of the
FABRO_HTTP_PROXY_POLICYvalue. Every common construction path still validates that boundary before use; I found no material meaning or validation friction. - Why 1-2 do not fit: There is one stable meaning, one resolver, and no recurring conversion through an invalid intermediate.
- Rule discrimination: Decision rule 4 could be read ambiguously if every
low-level builder method is called a policy escape hatch. The rubric's own
ProxyPolicyexample resolves that ambiguity in favor of the closed, validated environment-policy model.
duplication-knowledge: 3 (Medium)
- Evidence:
define_builder!atsrc/lib.rs:72-193is one authoritative production mechanism for the shared async/blocking builder surface and for applying the resolved proxy policy. The four convenience constructors route through those builders. The remaining repeated knowledge is narrow:"system"and"disabled"appear both in the parser and in the manually maintainedInvalidProxyPolicyexpectation text (src/lib.rs:29-35,63-69). - Strongest counterevidence: The macro removes the materially risky
async/blocking synchronization, and the compiler forces the policy-application
match to cover every enum variant. The two test helpers' use of
ProxyPolicy::Disabledis ordinary reuse, not a second policy authority. - Why 4 does not fit: The user-facing valid-value list is a small second representation that can drift from the parser, so there is some isolated repeated domain knowledge.
- Why 2 does not fit: There is no direct evidence that routine changes repeatedly synchronize separate async/blocking implementations. A future enum variant is hypothetical, and rule 5 specifically says exhaustive compiler-checked branches and hypothetical variants do not establish competing authorities.
- Rule discrimination: Rule 5 cleanly rules out 2 but is non-discriminating between 3 and 4 for a duplicated allowed-value error message. I treat that message as real but isolated maintenance friction, hence 3.
fabro-web-app
ownership-boundaries: 4 (Medium)
- Evidence:
app/entry.tsx:17-48owns root creation, global SWR policy, build-version guarding, toast mounting, and the single normal/install router choice.app/router.tsx:97-184owns normal route composition, whileapp/install-router.tsx:6-22owns the install graph. Shared HTTP translation and unauthorized handling live inapp/lib/api-client.ts:213-309; shared reads such asuseRunlive inapp/lib/queries.ts:182-187; recurring run mutations and cache follow-up live inapp/lib/mutations.ts:65-149. Route components compose these owners. Separately,scripts/build.ts:183-249,289-368contains the complete app-local build, atomic publication, and old-build pruning lifecycle and publishes onlyapps/fabro-web/dist; boundary tooling mirrors that output into the Rust SPA rather than the web build writing across the boundary. - Strongest counterevidence: Some route-specific CRUD mutations import
apiDataand generated API objects directly, and the install feature spansinstall-app.tsx,install-api.ts,install-query.ts, and effect hooks.run-detail.tsxis also a busy composition point. - Why 3 does not fit: The direct calls remain at the route-specific UX owner and still use the shared transport/error boundary; shared read and recurring run-lifecycle responsibilities are not reimplemented there. Install state, transport, query, and browser effects have distinct homes. I found no isolated lifecycle that must leave its owner and resupply identity.
- Why 1-2 do not fit: Runtime, routing, transport, queries, route UX, and build publication all have stable owners with dependencies pointing from composition toward shared services.
- Rule discrimination: The final repository example is useful and discriminating: a large route is not by itself boundary leakage. The score would change if direct routes reimplemented shared transport or cache lifecycles, but representative boundary checks did not show that.
repository-ci
ownership-boundaries: 4 (Medium)
- Evidence:
.github/workflows/rust.yml:48-147owns Rust formatting, lint/architecture checks, generated docs, Linux tests, twin E2E selection, and manual macOS tests..github/workflows/typescript.yml:36-77owns web and generated-client typechecks, web tests, and the production embedded-SPA integration build. Each workflow owns its concurrency and least-privilege job permissions. The TypeScript workflow'scargo dev buildis the intentional integration boundary that consumes the web bundle; it does not create a competing implementation of the web build. - Strongest counterevidence: The TypeScript build job invokes Rust build
tooling, path scopes overlap around
lib/apps/fabro-spa/**, and.github/zizmor.ymlis configuration whose consumer is not shown in these files. - Why 3 does not fit: Cross-language integration is part of the mapped CI purpose and has one concrete home. The stale configuration values discussed below are domain-model findings, while duplicated push/pull selectors are duplication findings; counting either again as ownership friction would violate the rubric's primary-lens rule.
- Why 1-2 do not fit: The Rust and TypeScript responsibilities and their dependency direction are stable. Routine validation changes have an obvious workflow owner rather than requiring competing lifecycle owners.
- Rule discrimination: The instruction not to penalize an unevidenced missing lifecycle matters for the unseen zizmor consumer. The rubric is otherwise discriminating once repeated selector policy is kept out of the ownership lens.
domain-model: 2 (High)
- Evidence: Both Rust trigger selectors name
openapi/**(.github/workflows/rust.yml:6-19,22-35), but that revision has no trackedopenapi/target. The actual Rust generator and TypeScript generator consumedocs/public/api-reference/fabro-api.yaml(lib/foundation/fabro-api/build.rs:159andlib/packages/fabro-api-client/package.json:7), a path omitted from both workflow trigger models. This makes a core API-spec change invisible to the intended CI trigger. In addition, all three.github/zizmor.yml:4-6line selectors target.github/workflows/rust.ymllines 37, 49, and 62, which are respectivelyworkflow_dispatch, thefmtjob key, and a shellrun, not action references forstale-action-refs. - Strongest counterevidence: Most configured branches, paths, action SHAs, runner labels, job names, and commands have clear current targets, and both workflow documents have a stable overall schema.
- Why 3 does not fit: The dead OpenAPI selector sits in both central Rust push and pull-request triggers and omits the actual source of truth. It is not merely an isolated stale lint suppression.
- Why 1 does not fit: The CI configuration language and almost all values remain interpretable; the problem is recurring invalid/no-target identifiers, not the absence of a stable configuration model.
- Rule discrimination: Decision rule 6 correctly classifies the no-target identifiers as domain pressure, but it does not itself distinguish 2 from 3. The centrality of the API source-of-truth trigger is what selects 2.
Control: fabro-checkpoint
ownership-boundaries: 2 (High)
- Evidence: Inside the component,
BranchStoreowns a branch string and author and delegates Git objects toStore(src/branch.rs:17-82), which is a sensible direction. At the production boundary, however, no production caller constructsBranchStore.fabro-workflow/src/run_metadata.rs:272-451instead keepsStore, branch, author,parent_oid, and discovery state as separate fields, manually writes blobs and trees, supplies parents toStore::write_commit, resupplies the branch toStore::update_ref, and owns fetch/push discovery. Other checkpoint commit and trailer lifecycle work also remains infabro-workflow. Thus the mapped checkpoint/metadata-branch lifecycle crosses the scoped owner on the normal production path. - Strongest counterevidence:
Storeis itself a mapped public entry point, the dependency direction remainsfabro-workflow -> fabro-checkpoint, and remote authentication/push orchestration reasonably belongs near a workflow run rather than in a low-level Git object store. - Why 3 does not fit: The caller-held branch and parent identity are used on every metadata snapshot, not only in an isolated migration or uncommon fallback.
- Why 1 does not fit: Low-level Git ownership and the higher workflow orchestration are both stable and understandable; they simply split one routine persistence lifecycle.
- Rule discrimination: Decision rule 2 is directly discriminating:
RunMetadataWriterretains and repeatedly resupplies the identity needed to complete operations onStore. The mapped breadth of “metadata branches” makes this more than ordinary parameter passing.
simplicity: 3 (Medium)
- Evidence: The production low-level path is traceable:
Store::write_blob -> TreeEntries::set -> Store::write_tree -> Store::write_commit -> Store::update_ref(src/git.rs:123-188).BranchStore::write_withalso gives branch-oriented writes one linear read/modify/write implementation (src/branch.rs:56-117). The recursive flat-tree conversion is justified by Git's nested tree representation. The friction is isolated:BranchStoreis a sizeable second entry layer with tests but no production caller at this revision, andCargo.toml:18declaresfabro-storealthough scoped production code does not reference it. - Strongest counterevidence: The two entry points represent legitimate
abstraction levels, and the mapped cartography names both. None of the normal
Storeoperations requires navigating configuration machinery or dynamic dispatch. - Why 4 does not fit: The unused higher layer/dependency is concrete, avoidable surface and configuration burden, even though it is off the current production common path.
- Why 2 does not fit: Routine production writes do not repeatedly choose
between
StoreandBranchStore; the observed caller consistently usesStore, and that path is direct. - Rule discrimination: The “public method alone does not establish
frequency” rule prevents treating
BranchStoreas a competing common path. It is less discriminating between 3 and 4; the concrete unused dependency and unused entry layer are why I select 3.
domain-model: 2 (High)
- Evidence:
GitAuthor::from_optionsaccepts arbitrary name/email strings (src/author.rs:22-30), whileBranchStore::newonly interprets them by callingSignature::now(...).expect(...)(src/branch.rs:26-39).TreeEntriesstores paths as unrestrictedStringandBranchStore::write_entry/write_entriesput caller strings into it without validation (src/git.rs:46-90,src/branch.rs:84-109); interpretation and possible rejection occur later while rebuilding Git trees.FileMode::from_i32also maps every unknown Git mode toBlob(src/git.rs:21-36) rather than preserving or rejecting an unknown shape. These invalid-capable intermediates sit on the mapped storage entry paths. - Strongest counterevidence:
FileModeis closed for values the component writes, normal metadata callers validate paths before constructingTreeEntries, Git itself rejects malformed signatures/trees, and object IDs use git2's typedOid. - Why 3 does not fit: Raw author and path values are carried by the ordinary entry-point types and interpreted later; they are not confined to a separate compatibility importer.
- Why 1 does not fit: Authors, tree entries, modes, branches, and commits all have stable intended meanings. The issue is delayed validation and lossy fallback, not an unidentifiable core concept.
- Rule discrimination: Decision rule 4 is discriminating here: these are common-path invalid-capable intermediate shapes rather than a low-level escape hatch unused by the entry path.
duplication-knowledge: 3 (Medium)
- Evidence: Important transformations are mostly authoritative:
FileMode::{as_i32,from_i32}contains the mode mapping,BranchStore::write_withcontains branch read/modify/write, andGitAuthor::defaultcontains the default identity. The narrow repeated knowledge is the bare-branch to full-ref transformationformat!("refs/heads/{branch}")in each ofStore::{update_ref,resolve_ref,delete_ref}(src/git.rs:182-225), with another full-ref rendering at the direct workflow metadata boundary. Trailer rendering also spells"{}: {}"in bothappendandformat_message(src/trailer.rs:9-65). - Strongest counterevidence: The repeated ref syntax is stable low-level Git syntax, the three ref methods implement different operations, and the apparent duplication in single-entry/multi-entry or tip/commit reads has intentionally different result shapes. Unifying those operations would risk a parameterized mega-helper.
- Why 4 does not fit: Full-ref and trailer-line rendering have small but real second representations rather than one helper/type enforcing each transformation.
- Why 2 does not fit: There is no direct evidence of routine changes repeatedly synchronizing those stable renderings, and hypothetical future ref methods do not satisfy decision rule 5. The repeated knowledge is isolated from ordinary checkpoint-format extension.
- Rule discrimination: Rule 5 usefully rules out 2 but is non-discriminating between 3 and 4 for repeated, stable protocol syntax. I score 3 because the repetitions are concrete, while keeping confidence Medium because their maintenance materiality is limited.
Round 2 revalidation
I independently reapplied the simplified decision rules to only the requested assignments. Scores below supersede the corresponding Round 1 judgments for this revalidation.
| Component | Lens | Round 2 score | Confidence |
|---|---|---|---|
fabro-http |
duplication-knowledge | 3 | High |
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 (High)
- Decisive evidence:
define_builder!remains the one mechanism for the materially recurring async/blocking builder policy (src/lib.rs:72-193). The parser andInvalidProxyPolicymessage still hold a concrete second representation of the allowed"system"/"disabled"vocabulary (src/lib.rs:29-35,63-69). - Adjacent scores: 4 does not fit because revised rule 6 explicitly caps a concrete second semantic representation at 3. Score 2 does not fit because an ordinary mapped change does not currently synchronize separate async and blocking implementations; adding a future policy variant is not direct recurrence evidence.
- Remaining ambiguity: None material. Revised rule 6 now resolves the prior 3-versus-4 uncertainty.
repository-ci × ownership-boundaries: 2 (High)
- Decisive evidence: The Rust workflow's architecture check scans
apps,lib/packages, anddocs/public/api-reference/fabro-api.yaml(.github/workflows/rust.yml:80-91), but its push and pull-request triggers omit all three routine target paths (rust.yml:6-19,22-35). Its Cargo jobs also consume the real API specification throughlib/foundation/fabro-api/build.rs, yet that specification does not trigger the workflow. The TypeScript workflow likewise consumes the generated API client and performs the embedded integration build without making the source specification a trigger. Under revised rule 3, each check owns this coverage; the omitted routine targets are therefore central ownership pressure. - Adjacent scores: 3 does not fit because API, app, and package changes are routine targets of checks the workflow actually runs, not isolated edge inputs. Score 1 does not fit because Rust and TypeScript job ownership and dependency direction otherwise remain stable.
- Remaining ambiguity: None material. The nonexistent
openapi/**value is still a separate domain-model finding; the ownership finding rests on the real scanned/consumed paths that fail to trigger.
fabro-checkpoint × ownership-boundaries: 2 (High)
- Decisive evidence: The mapped higher owner is
BranchStore, but the routine production metadata caller instead retainsStore, branch, author, parent, and discovery state and reconstructs blob/tree/commit/ref lifecycle fromStoreprimitives infabro-workflow/src/run_metadata.rs:272-451. Revised rule 2 names this shape directly. - Adjacent scores: 3 does not fit because reconstruction occurs on every
metadata snapshot, not at an isolated edge. Score 1 does not fit because the
low-level
Storeand workflow-level caller are stable, identifiable owners; the concern is the lifecycle split between them. - Remaining ambiguity: The workflow reasonably owns remote authentication, but that does not remove its reconstruction of the mapped checkpoint and metadata-branch persistence lifecycle.
fabro-checkpoint × simplicity: 3 (High)
- Decisive evidence: The current production
Storewrite sequence is linear and direct (src/git.rs:123-188).BranchStoreis a parallel mapped entry layer with no production caller at this revision, andCargo.toml:18declares the unused production dependencyfabro-store. Revised rule 4 classifies exactly this as isolated simplicity friction that caps 4 at 3. - Adjacent scores: 4 does not fit because the parallel unused layer and
dependency are concrete. Score 2 does not fit because routine callers do not
navigate competing paths or machinery; they consistently follow the direct
Storepath. - Remaining ambiguity: None material after rule 4.
BranchStorebeing a mapped entry does not make it frequent when the boundary search finds no production caller.
fabro-checkpoint × domain-model: 2 (High)
- Decisive evidence: Mapped entry shapes accept unrestricted author and path
strings:
GitAuthor::from_optionsstores raw values beforeBranchStore::newinterprets them withSignature::now(...).expect(...)(src/author.rs:22-30,src/branch.rs:26-39), andTreeEntries/write_entrycarry unchecked string paths until Git-tree construction (src/git.rs:46-90,src/branch.rs:84-109). Revised rule 5 says caller validation and a typed destination do not isolate this invalid-capable mapped entry. - Adjacent scores: 3 does not fit because the invalid-capable shapes are on mapped entry paths, not a compatibility-only edge. Score 1 does not fit because the intended meanings of authors, paths, modes, and commits remain stable.
- Remaining ambiguity: None material. Normal callers supplying valid values does not make the entry type canonical by construction.
fabro-checkpoint × duplication-knowledge: 3 (Medium)
- Decisive evidence: Bare branch names are independently rendered as
refs/heads/{branch}inStore::update_ref,resolve_ref, anddelete_ref(src/git.rs:182-225), and trailer lines are independently rendered intrailer::appendandformat_message(src/trailer.rs:9-65). These are concrete second semantic representations, so revised rule 6 excludes 4. - Adjacent scores: 4 does not fit because the second renderings are real. Score 2 does not fit because no evidenced ordinary mapped change must synchronize the stable Git ref or trailer syntax across those locations; future ref operations are hypothetical, while the existing operations have distinct behavior.
- Remaining ambiguity: Limited ambiguity remains over whether stable protocol syntax is material enough to count as semantic repetition at all. Rule 6 does not define that threshold, so confidence remains Medium; if it counts, 3 is the rule-directed score.