Skip to content

fix(architecture-audit): repair reachability drift detection (#187) - #1215

Draft
0xPlayerOne wants to merge 5 commits into
mainfrom
f2w3/cp187-architecture-audit
Draft

0xPlayerOne wants to merge 5 commits into
mainfrom
f2w3/cp187-architecture-audit

Conversation

@0xPlayerOne

@0xPlayerOne 0xPlayerOne commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

Draft PR published from the verified owner commit; auto-merge is off. This description records the bounded contribution and remaining acceptance gates.

  • Task: 20261021-f2w3-cp187-binding-and-evidence-path-repair-04
  • Native turn: d5356e88-2e73-49c1-bd22-4b9f8b2be4ac; terminal done, exit 0, cleanup confirmed.
  • Branch/worktree: f2w3/cp187-architecture-audit / /tmp/opencode-fleet-2-20261021/worktrees/F2W3.
  • Base: 27f17bd55e0266439b5b3089163195c9e7a669be; head: fc169f506ec98432883af6cd26bcc8a0101570f3; tree: 7e4f42e071f382b87ce8b9a46d3d95a32311dcae; clean; base ancestor verified.
  • Full base-to-head binary diff SHA256: 9e152c877711f268c6e691140cdc3371db22f0edc302f91157bf38b7f63b46be.
  • Manifest (4 paths; +2,090/−37):
    • A docs/evidence/m11-architecture-audit-binding-detector-handoff-2026-10-21.md
    • A docs/evidence/m11-architecture-reachability-trace-2026-10-21.md
    • M scripts/architecture-audit.mjs
    • A scripts/m11-architecture-audit-regression.test.mjs
  • Terminal checks: F2W3 regression 25 pass / 184 assertions; W1 architecture tests 7 pass / 193 assertions; smoke 629 pass / 5 skip / 0 fail; lint/format/boundaries/digest/canonical-ordering/DB/compatibility/OpenAPI/infra checks passed. PostgreSQL and authenticated requirements checks remain external.
  • Remaining acceptance: exact-head root review, CI lane registration handoff to runner owner, and final candidate integration. Existing PR fix(architecture-audit): repair reachability drift detection (#187) #1215 is updated to this exact head as a draft. Issue M19.02 — Audit architecture, contracts, persistence, and production wiring #187 remains open.
  • Publication: existing PR fix(architecture-audit): repair reachability drift detection (#187) #1215 updated to this exact head as a draft; auto-merge off. Root exact-head review and CI lane ownership remain open; no closure/readiness claim.

Follow-up: cache-isolation regression repair

Published checked owner commit 70017bd9ae7a46a8a58aada372f9e75166004d02 (tree 824101a05885d18569cb48f8fb7acfbf51dee4a4), three files. It adds a deterministic regression for same-path/same-size/same-mtime source roots with different content; the architecture cache now keys by canonical path and validates content SHA-256.

Checks: focused regression 26/0 (188 assertions); W1 architecture regression 7/0 (193 assertions); bun run architecture:check, changed-file format and lint passed. No full build, coverage, or smoke run. Profile-source scan observed 20 shorthand bindings and no lexical-scope mismatches; live/production acceptance remains open. Patch SHA-256: 9972ee2159c81b5ad8018ce3e7d4e2138cc8627c4b084729ccd6083ae64643aa. This is still a draft and not an issue closure.

…it with regression checks

The CP #187 reachability audit could not detect several real drifts, and its
controller discovery reported reachability for routes the API does not serve
under the operation's own HTTP verb.

Repairs in scripts/architecture-audit.mjs:

- Controller routes are discovered per HTTP verb and carry their method, so
  reachability matches method and path instead of the path alone. The Nest
  URI-versioning defaults are read from the application, so a controller that
  pins VERSION_NEUTRAL is served unversioned instead of being dropped.
- Profile port bindings are compared as a map, so a reordered but identical
  binding map is no longer read as drift.
- Evidence must be a repository-relative regular file inside the audited
  checkout; absolute and escaping paths and directories are rejected.
- Only HTTP verbs are read from an OpenAPI path item, and an OpenAPI
  operation with no SDK binding is now an error.
- Served routes with no SDK binding, the Control API capability bindings that
  default to an unavailable implementation, and the per-profile set of
  capabilities a profile never binds are reported as warnings.
- The persistence parity inventory must agree with the parity rows it
  summarises; the Bun lockfile workspace version is compared with the
  manifest; and the migration registry of both dialects is measured, with a
  PostgreSQL registry whose SQL files and journal disagree treated as an
  error.
- A profile pair that shares one composition source digest is reported.

The rendered report is unchanged, so the canonical architecture artifact and
its generated report stay with their current owner.

New scripts/m11-architecture-audit-regression.test.mjs holds 16 deterministic
negative drift checks that fail closed on each of these conditions. It sits
next to the tool it covers because it imports the audit script directly, and
the workspace boundary rule rejects an import that leaves a package directory;
a copy under tests/ would also require lane registration this work does not
own.

New docs/evidence/m11-architecture-reachability-trace-2026-10-21.md records the
four-profile reachability trace, the final-candidate readback of the
canonical audit against the current tree, the contract/OpenAPI/schema/
migration/SDK and SQLite/PostgreSQL parity matrices, the dead ends and
disabled bindings, the independent re-verification of the retained
state-machine, race and restart evidence, and the findings routed to their
exact source owners.
The capability-binding detector matched names, so it read a commented example,
a type-only declaration and an explicit `undefined` binding as a runtime
binding. That can conceal a missing default: a capability named only in a
comment, only in a type, or bound to `undefined` is in fact served by the
Control API from its unavailable implementation.

bindsCapabilityOption now reads the source with the TypeScript parser, so the
language decides what is code, what is a type and what is a comment. A
capability counts as bound only when a runtime object literal assigns it: a
property assignment with a real value, or a shorthand property whose value is
not `undefined`. A name in a comment, a `type` alias member, an `interface`
member, a class property signature, a member access and an explicit
`undefined` are not bindings.

The measured per-profile binding matrix is unchanged: cloud binds 20 of the
21 unavailable-default capabilities, hosted-server 13, hosted-simple 16 and
local 16, and the same capabilities remain unbound in each. The complete
output of the architecture audit is byte-identical to the previous head.

The parse is memoised per file identity (size and modification time), so
repeated discovery in one process does not re-parse the tree.

The dedicated regression file grows from 16 to 22 tests: one per reported
finding (commented example, type-only declaration, explicit `undefined`), one
for the positive runtime cases, one proving a commented binding cannot
conceal a missing default in a real profile source, and one pinning the
measured per-profile counts.

docs/evidence/m11-architecture-audit-binding-detector-handoff-2026-10-21.md
records the correction, the proof that the measurement did not change, the
gate results and the proposed CI lane and owner handoff, which is proposed
only and not applied.
…by symlink

The evidence check resolved a path against the repository root, verified the
result lexically and then followed symlinks for the file test. A symlink
inside the repository that points outside it was therefore accepted, so an
in-tree evidence path could cite a file anywhere on the host.

validateEvidence now delegates to evidencePathError, which compares the
repository root and the evidence target as canonical real paths. The root is
canonicalised too, so a checkout reached through a symlinked path does not make
every in-tree path look like an escape. The target is canonicalised before it
is examined and the file test is applied to the canonical target, so no
symlink is followed to a file outside the root. A target that cannot be
resolved, for example a symlink loop, is rejected instead of being mistaken
for a missing file.

Ordinary in-tree evidence is unchanged: the repository has no symlink outside
node_modules and dist, all 116 evidence paths in the canonical audit are
regular existing files, and the canonical audit still validates with zero
errors. The complete output of the architecture audit is byte-identical to the
previous head.

The dedicated regression file grows from 22 to 25 tests. The new evidence
tests are hermetic: they build a synthetic repository root in a temporary
directory with a synthetic target outside it, a symlink from the root to that
target, an ordinary in-tree file, an in-tree symlink that stays inside the
root, and a symlink that cannot be resolved. The external target holds only
synthetic text, no real or sensitive file is read, and nothing is written into
the checkout.

This bundles into the same branch and draft pull request as the
binding-parser repair. The handoff packet now covers both root findings, the
proof that neither changes a measured value, the gate results and the proposed
CI lane and owner handoff, which is proposed only and not applied.
… parsed

The parsed-source cache was keyed by the relative path and validated by
modification time and size, while the parse target was resolved against the
checkout root. Sequential audits of two checkout roots could therefore reuse
the wrong checkout's parse: the matching files had the same relative path, the
same size and the same timestamp, and the second root reported the first
root's answer.

The cache entry is now keyed by the canonical target path and validated by the
SHA-256 of the file contents, so an entry is reused only for the same
canonical file with the same bytes. The parse is still reused, so the cost does
not regress.

The new regression is hermetic and deterministic: two synthetic checkout roots
are written with contents of exactly the same byte length, one binding the
capability and one binding it to undefined, and every file is given the same
fixed modification time with utimes. The fixture asserts the two lengths match.
Auditing the first root and then the second root in one process must produce
different answers; against the previous cache the second assertion fails.

The handoff packet records the scope inspection root asked for. The six
admitted profile sources contain 20 shorthand capability bindings, each with a
unique declaration that encloses the shorthand site, so no admitted source
exercises shorthand shadowing or a lexical-scope divergence. The conservative
first-declaration rule is therefore exact for the admitted sources; its limits
and both possible error directions are documented, together with the pinned
per-profile counts that would fail if a composition started declaring a
capability name twice.

This preserves the reviewed comment, type and undefined parser fixes and the
realpath evidence-containment regression. The measured capability matrix is
unchanged and the architecture audit output stays byte-identical to the
previous head.
…ions

The regressions for the three reviewed repairs are extended from the published
draft pull request head, and the owned trace and handoff packets are finished.

The parse cache now proves both halves of its identity. The existing check
covers two checkout roots whose matching files share a relative path, byte
length and modification time; it now also audits the first root again
afterwards, so the second root cannot have overwritten the first root's entry.
A second check rewrites the same paths with the same byte length and the same
modification time and requires the sources to be parsed again, because the
content hash is the change identity. Both checks fail against the previous
relative-path keyed cache.

The lexical-scope limit is pinned honestly. The detector resolves a shorthand
property by taking the first declaration of that name anywhere in the file, so
the answer follows file order rather than lexical scope. The new test shows an
inner shorthand that binds undefined in both orderings being reported as not
bound in one and as bound in the other. The test records what the rule does; it
does not claim the rule is right, and it keeps the limit visible.

The architecture trace packet is finished: it labels every per-profile
reachability, capability binding and port statement as a static inventory of
the sources rather than a runtime observation, records the three corrections
made after the base head, updates the drift-check inventory to the current 28
tests, and separates the focused static checks from the wider suite that needs
cross-fleet capacity to be rechecked first.

The handoff packet records the pinned dependency resolution and the toolchain
state, and the extended regressions.

No runtime-profile proof is claimed and CP #187 is not claimed complete.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant