Skip to content

docs(spec): reconcile the feature 010 defects found during implementation - #87

Merged
mbeacom merged 1 commit into
mainfrom
mbeacom-supreme-guacamole
Aug 5, 2026
Merged

docs(spec): reconcile the feature 010 defects found during implementation#87
mbeacom merged 1 commit into
mainfrom
mbeacom-supreme-guacamole

Conversation

@mbeacom

@mbeacom mbeacom commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Fifteen defects found by the four implementation sessions reading the specs against each other and against the ADRs. Every one is in an artifact the planning work produced, not in the implementations. Specs only — no code, no task text beyond the corrections described here.

The serious one

contracts/admissibility.md §2 gave metadata.namespace the same character class as metadata.name[A-Za-z0-9] plus -, _, ..

ADR-0015 binds it through CommonValidatorFunctions.isValidDnsLabel, /^[a-z0-9]+(?:\-+[a-z0-9]+)*$/no uppercase, no underscore, no dot. An implementation written to my summary would have silently admitted namespaces the pinned validator rejects, and then canonicalized them: exactly the defect ADR-0015 exists to prevent.

Phase D followed the ADR over the contract, per FR-016's "exactly the four field validators in ADR-0015's table". Verified empirically rather than by reading:

validateNamespace('Default') = false   ✓
validateNamespace('a_b')     = false   ✓
validateNamespace('default') = true    ✓

The contract now matches the code, and carries the discriminating assertion so the error can't recur silently.

Two more of the same shape

  • data-model.md §4's AdmissibilityField was wrong in both directions — it omitted apiVersion, which ADR-0015 requires, and added spec.type, which no validator in that table covers.
  • §3's DescriptorDocument omitted apiVersion entirely, so a record built to spec could not be admissibility-checked at all. Now carries rawApiVersion and raw.

provenance gets a closed domain

upstream-authored | maintainer-overlay, decided by the maintainer. It previously typed as a bare string while consumer step 2 required "a recognized provenance" — an unsatisfiable pairing that blocked Phase E.

The semantics needed care: under ADR-0020 clause 5 the descriptors are upstream-authored in both cases, so the value describes the annotation's provenance, not the descriptor's. It also doubles as a live adoption signal — zero third-party descriptors carry the annotation today, so upstream-authored only becomes reachable if real adoption occurs.

Five steps, three reasons

T058, T062 and SC-006 all conflated decode steps with rejection reasons. The frozen contract defines five steps but three reasons: step 1 yields the annotation-absent state rather than a rejection, and step 5 delegates to the glob dialect (SC-007's territory).

Phase D left both tasks unchecked rather than manufacture two reasons no contract froze. All three artifacts now say five steps, three reasons, and name them.

Four carried-forward conflicts, resolved without editing spike 009

Recorded in contracts/README.md §4, since spike 009 stays frozen: the namespace class, the unrecognized-manifest-field trigger class, path-validation stage 1's unnamed trigger, and requiredCapabilities arity.

§4.5 records the one place transcription from ADR-0015 was insufficient — the table gives isValidDnsSubdomain's bounds but never its per-label character class — and labels the composition an inference, not a transcription. It's a candidate for an ADR-0015 addendum.

Line numbers → reason strings

Phase A's own allowlist additions shifted both check-deps guards by 41 lines, silently invalidating every citation in tasks.md and in normative package-boundary.md §4.

All citations now name the emitted reason string. A line number is a reference nothing checks; a reason string is asserted verbatim by the test suite, so a stale citation fails the build rather than quietly misleading a reader. The rule is written into the contract as §5.

Also

  • T013 now creates negative-cases/ and states it is a shared cross-phase tree whose per-task subdirectories are what make it safe for concurrent worktrees. No task created it before, despite ~20 tasks depositing into it.
  • T027 gains a tenth fixture — the all-annotation-absent acceptance case required by snapshot-envelope.md §7 row 1b. Without it the set contains only rejections and cannot prove the validator does not over-reject.
  • T009 records that check:deps structurally cannot see schema/ (no package.json, so readWorkspacePackages() never visits it). That clause is held by the isolated linker and observed via typecheck. SC-015 is still discharged, because it requires "the isolation check" to fail — but the deviation is named, not smoothed.
  • T100 must match claims, not vocabulary. ADR-0014's terms are binding, so the most honest phrasing — "not reference-verified (rung 2)" — contains the exact strings a naive grep would flag. A check failing on bare occurrence punishes the most honest documentation and rewards silence.

Checks

bun test 1495 pass / 0 fail · typecheck clean · check:depscore-has-no-adapter-deps: ok · adr lint 20 records, 0 errors.

Still open, and deliberately not decided here

  • The barrier branch (mbeacom-t019-independent-audit @ c8e47dd) cannot be pushed by this session — T022 wires the drift check into .github/workflows/ci.yml and the token lacks workflow scope. Only that one commit is affected.
  • D9: whether reading node_modules/picomatch/package.json from disk is the intended way to satisfy T063's "read at runtime, never transcribed" without tripping Phase A's import.meta.resolve ban.
  • D10: Phase A's source-scan.ts importSpecifiers heuristic false-positives — it read the string 'bulk-import' as an import. Tighten it, or extend EXCLUDED_FROM_SCAN.
  • The locality-guard conflict between Phase A's scan and T026/T037.

Standing: ADR-0014 rung 1 only. No rung-2 or rung-3 claim, no release scheduled or prepared. ADR-0012 gate 3 open; gate 4 unmet and not yet testable.

…tion

Fifteen defects found by the four implementation sessions reading the specs
against each other and against the ADRs. Every one is in an artifact this
planning work produced, not in the implementations. Specs only; no code.

The serious one: contracts/admissibility.md §2 gave metadata.namespace the
same character class as metadata.name. ADR-0015 binds it through
CommonValidatorFunctions.isValidDnsLabel, /^[a-z0-9]+(?:\-+[a-z0-9]+)*$/,
which admits no uppercase, no underscore and no dot. An implementation
written to the summary would have silently admitted namespaces the pinned
validator rejects, then canonicalized them - exactly the defect ADR-0015
exists to prevent. Phase D followed the ADR over the contract, and the
implementation is empirically correct: validateNamespace('Default') is
false, 'a_b' is false, 'default' is true. The contract now matches.

data-model.md had two more of the same shape. §4's AdmissibilityField was
wrong in both directions - it omitted apiVersion, which ADR-0015 requires,
and added spec.type, which no validator in that table covers. §3's
DescriptorDocument omitted apiVersion entirely, so a record built to spec
could not be admissibility-checked at all.

provenance is now a closed domain: upstream-authored | maintainer-overlay,
decided by the maintainer. The semantics needed care, because under
ADR-0020 clause 5 the descriptors are upstream-authored in both cases - so
the value describes the ANNOTATION's provenance, not the descriptor's. It
also doubles as a live adoption signal: zero third-party descriptors carry
the annotation today, so upstream-authored only becomes reachable if real
adoption occurs.

T058/T062/SC-006 conflated five decode STEPS with five rejection REASONS.
The frozen contract defines three - step 1 yields the annotation-absent
state rather than a rejection, and step 5 delegates to the glob dialect.
Phase D left both tasks unchecked rather than manufacture two reasons no
contract froze; the tasks now say five steps, three reasons.

Four carried-forward contract conflicts are resolved in contracts/README.md
§4 rather than by editing spike 009, which stays frozen: the namespace
class, the unrecognized-manifest-field trigger class, path-validation stage
1's unnamed trigger, and requiredCapabilities arity. §4.5 records the one
place transcription from ADR-0015 was insufficient - the table gives
isValidDnsSubdomain's bounds but never its per-label character class - and
labels the composition an inference rather than a transcription.

Line-number citations are replaced by the emitted reason strings. Phase A's
own allowlist additions shifted both check-deps guards by 41 lines,
silently invalidating every citation. A line number is a reference nothing
checks; a reason string is asserted verbatim by the test suite, so a stale
citation fails the build instead of quietly misleading a reader.

Also: T013 now creates negative-cases/ and states that it is a shared
cross-phase tree whose per-task subdirectories are what make it safe for
concurrent worktrees; T027 gains the tenth fixture (the all-annotation-absent
acceptance case, without which the set contains only rejections and cannot
prove the validator does not over-reject); T009 records that check:deps
structurally cannot see schema/, which has no package.json, so that clause
is held by the isolated linker and observed via typecheck; and T100 must
match claims rather than vocabulary, since ADR-0014's binding terms mean the
most honest phrasing contains the exact strings a naive grep would flag.

Gates: bun test 1495 pass / 0 fail, typecheck clean, check:deps ok,
adr lint 20 records 0/0.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings August 5, 2026 13:08
@mbeacom
mbeacom merged commit 77d2b08 into main Aug 5, 2026
10 checks passed
@mbeacom
mbeacom deleted the mbeacom-supreme-guacamole branch August 5, 2026 13:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Reconciles feature 010 specifications with ADRs and implementation findings.

Changes:

  • Corrects admissibility fields and namespace validation.
  • Defines annotation provenance and contract precedence.
  • Updates validation tasks, fixtures, and dependency-guard citations.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tasks.md Revises implementation and evidence tasks.
spec.md Clarifies annotation decoding criteria.
data-model.md Updates descriptor, admissibility, and provenance models.
contracts/README.md Records carried-forward contract conflicts.
contracts/package-boundary.md Replaces line-number guard citations.
contracts/admissibility.md Corrects namespace validation rules.

provenance: AnnotationProvenance
}

AnnotationProvenance = "upstream-authored" | "maintainer-overlay"
Comment on lines +228 to +230
**§2's rejection rule governs.** An arity rejection the contract does not authorize must
not be invented, so `[]` and repeated entries do **not** trigger rejection. `data-model.md`
§1's tuple should be read as the expected shape, not as an additional validation rule.
Comment on lines +1111 to +1113
- **SC-006** *(annotation decode order)*: The annotation's **five** ordered decode/validate
steps are evaluated in order, and the **three** steps that can reject each produce their own
distinct rejection reason when violated in isolation — `annotation-value-not-a-string`
- [ ] T062 [US4] SC-006 close-out: a consolidated test over the five annotation decode
steps, each rejecting at its own step with its own reason.
- [ ] T062 [US4] SC-006 close-out: a consolidated test over the **five** annotation decode
steps, asserting that the **three** rejecting steps each reject at their own step with
Comment on lines +449 to +450
without it the fixture set contains only rejections, which cannot prove the
validator does not **over**-reject.
Comment on lines 125 to 126
The two guards in `scripts/check-deps.ts` that matter here (all line numbers read in this
worktree):
Comment on lines +231 to +233
A line number is a reference that nothing checks. A reason string is a reference the
test suite checks on every run, so a citation that goes stale fails the build rather
than quietly misleading a reader.
`<ADAPTER>/src/ownership/annotation.ts`, each with its own distinct rejection
reason. Observe each of the five failing independently; record five distinct
reason strings; restore; observe the pass.
`<ADAPTER>/src/ownership/annotation.ts`. **Three** of the five can reject, each with
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.

2 participants