docs(spec): reconcile the feature 010 defects found during implementation - #87
Merged
Conversation
…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>
There was a problem hiding this comment.
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 gavemetadata.namespacethe same character class asmetadata.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:
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'sAdmissibilityFieldwas wrong in both directions — it omittedapiVersion, which ADR-0015 requires, and addedspec.type, which no validator in that table covers.DescriptorDocumentomittedapiVersionentirely, so a record built to spec could not be admissibility-checked at all. Now carriesrawApiVersionandraw.provenancegets a closed domainupstream-authored|maintainer-overlay, decided by the maintainer. It previously typed as a barestringwhile consumer step 2 required "a recognizedprovenance" — 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-authoredonly 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-absentstate 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, andrequiredCapabilitiesarity.§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-depsguards by 41 lines, silently invalidating every citation intasks.mdand in normativepackage-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
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.annotation-absentacceptance case required bysnapshot-envelope.md§7 row 1b. Without it the set contains only rejections and cannot prove the validator does not over-reject.check:depsstructurally cannot seeschema/(nopackage.json, soreadWorkspacePackages()never visits it). That clause is held by the isolated linker and observed viatypecheck. SC-015 is still discharged, because it requires "the isolation check" to fail — but the deviation is named, not smoothed.Checks
bun test1495 pass / 0 fail ·typecheckclean ·check:deps→core-has-no-adapter-deps: ok·adr lint20 records, 0 errors.Still open, and deliberately not decided here
mbeacom-t019-independent-audit@c8e47dd) cannot be pushed by this session — T022 wires the drift check into.github/workflows/ci.ymland the token lacksworkflowscope. Only that one commit is affected.node_modules/picomatch/package.jsonfrom disk is the intended way to satisfy T063's "read at runtime, never transcribed" without tripping Phase A'simport.meta.resolveban.source-scan.tsimportSpecifiersheuristic false-positives — it read the string'bulk-import'as an import. Tighten it, or extendEXCLUDED_FROM_SCAN.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.