docs(agents): add contributor onboarding guide and editor rules file
This commit is contained in:
parent
ca0db3104f
commit
c4485ad6b5
@ -2,6 +2,10 @@
|
||||
|
||||
Guidance for anyone writing code in PureXML.
|
||||
|
||||
> Working in **Google Antigravity** (Gemini)? Start with `GEMINI.md` (always-on
|
||||
> rules) and `docs/onboarding-antigravity.md` (the full, literal, step-by-step
|
||||
> procedure for the schema-conformance work).
|
||||
|
||||
## Rule Loading
|
||||
|
||||
At the start of a session, read this file and the rules under `docs/rules/` that
|
||||
|
||||
72
GEMINI.md
Normal file
72
GEMINI.md
Normal file
@ -0,0 +1,72 @@
|
||||
# GEMINI.md, always-on rules for Antigravity agents in PureXML
|
||||
|
||||
These rules are always in effect. The full, step-by-step procedure is in
|
||||
**`docs/onboarding-antigravity.md`**, read it before your first change. When a
|
||||
rule here and a guideline elsewhere conflict, this file wins.
|
||||
|
||||
## The prime directive
|
||||
|
||||
PureXML is an XML/XSD validator. **Never reject a valid schema or a valid
|
||||
document.** In the conformance counts this is `valid-schemas-rejected`; it is
|
||||
**1** and must **never rise**. Under-rejecting (missing an invalid schema) is
|
||||
acceptable; over-rejecting (rejecting a valid one) is a failure and must not be
|
||||
merged. When unsure, do less.
|
||||
|
||||
## Hard constraints (never violate)
|
||||
|
||||
- No external SwiftPM dependencies. No C/C++/Objective-C/JavaScript/generated
|
||||
runtime. Pure Swift only.
|
||||
- Public API stays under the `PureXML` namespace tree.
|
||||
- Must build on macOS, Linux, and WASI.
|
||||
- Reference implementations are for **reading only, never copying** (clean-room).
|
||||
- Tests import only `Foundation`, `Testing`, and `@testable import PureXML`.
|
||||
|
||||
## Per-change procedure (do every step; details in the onboarding doc §6)
|
||||
|
||||
1. **Measure first**: read `/tmp/xsts-failures.txt`, pick a family, **read the
|
||||
actual `.xsd` files** before coding. Do one rule per change.
|
||||
2. Implement conservatively in `Sources/Schema/`; return `[String]` errors; wire
|
||||
into an aggregator in `SchemaIDAttribute.swift`.
|
||||
3. Add unit tests (one rejected case + several accepted cases). Write the test
|
||||
file **before** launching the release XSTS run.
|
||||
4. Run all gates (build, `swiftformat --lint`, `swiftlint --strict`,
|
||||
`check-style.sh`, `check-namespacing.sh`, `swift test`, `check-wasm.sh`).
|
||||
5. Run the conformance differential:
|
||||
`XSTS_ROOT=/private/tmp/xsts/xmlschema2006-11-06 swift test -c release --filter XSTS`.
|
||||
`invalid-schemas-accepted` must go DOWN and **no other bucket may rise**.
|
||||
6. Run an **adversarial critic** that tries to construct a valid schema your
|
||||
change rejects. Fix any false positive before merging. (Mandatory.)
|
||||
7. Update the pinned baseline in `Tests/XSTSSuiteTests.swift`, add a
|
||||
`CHANGELOG.md` entry, update `docs/schema-validity-burndown.md`.
|
||||
8. Branch off `main`, commit, `git merge --ff-only`, push to GitHub.
|
||||
|
||||
## The traps that recur (onboarding doc §8)
|
||||
|
||||
- **Namespace conflation:** resolve a name's namespace with
|
||||
`XSDNode.referenceNamespace(ref, bindings)`; never just strip the prefix. A
|
||||
built-in (`xs:*`) or imported component must not be confused with a
|
||||
same-local-name user one. This has caused false positives 7+ times.
|
||||
- **swiftformat vs swiftlint brace conflict:** extract a multi-condition `if`
|
||||
into a `Bool` helper; turn a long `for ... where` into `guard ... continue`.
|
||||
- **Limits:** file ≤ 400 lines, type body ≤ 250, cyclomatic ≤ 10, params ≤ 5,
|
||||
line ≤ 180. Split into new files/helpers; bundle args into a labeled tuple.
|
||||
- Read schema attributes **unprefixed** (`prefix == nil`); skip foreign-namespace
|
||||
nodes and `xs:appinfo`/`xs:documentation`.
|
||||
|
||||
## Commit discipline (never violate)
|
||||
|
||||
- Commit format: `type(scope): summary`. **No em dashes** anywhere.
|
||||
- **No AI attribution** of any kind in commits, PRs, branches, code, or docs (no
|
||||
"Generated with", no "Co-Authored-By", no mention of Gemini/AI/agent). All work
|
||||
reads as human-authored. (`docs/onboarding-antigravity.md` is the sole
|
||||
exception, explicitly requested.)
|
||||
- **Never push to a GitLab remote.** GitHub remotes only; check the URL first.
|
||||
- Merge to `main` `--ff-only`. The pre-push hook runs `swift test` and a
|
||||
commit-message check.
|
||||
|
||||
## Definition of done
|
||||
|
||||
All gates green; conformance differential shows invalid-accepted down and **no
|
||||
other bucket up** (valid-rejected still 1); the critic found zero false
|
||||
positives; baseline/CHANGELOG/burndown updated. If you rejected even one valid
|
||||
schema, the change is wrong regardless of how many invalid ones it caught.
|
||||
471
docs/onboarding-antigravity.md
Normal file
471
docs/onboarding-antigravity.md
Normal file
@ -0,0 +1,471 @@
|
||||
# Onboarding: working on PureXML in Google Antigravity (Gemini)
|
||||
|
||||
This document is written for an AI agent (Gemini) operating inside Google
|
||||
Antigravity. It is deliberately literal and exhaustive. **Do exactly what it
|
||||
says, in the order it says.** When a step says "run X", run X and read its
|
||||
output before continuing. Do not skip the verification steps. Do not infer; when
|
||||
this document gives a command or a rule, follow it verbatim.
|
||||
|
||||
Antigravity auto-loads `GEMINI.md` (repo root) and `AGENTS.md` as always-on
|
||||
rules. This file is the long-form companion: `GEMINI.md` is the short
|
||||
constitution, this file is the full procedure. Read both before your first
|
||||
change.
|
||||
|
||||
---
|
||||
|
||||
## 0. The one thing that matters most
|
||||
|
||||
PureXML is an XML/XSD validator. Its single most important quality is:
|
||||
|
||||
> **Never reject a valid schema or a valid document.**
|
||||
|
||||
In the conformance numbers (Section 5) this is the bucket
|
||||
`valid-schemas-rejected`. It is currently **1** and must **never go up**. A
|
||||
change that fixes ten bad schemas but wrongly rejects one good schema is a
|
||||
**failure** and must not be merged. When in doubt, do less: it is always
|
||||
acceptable to *miss* an invalid schema (under-reject); it is never acceptable to
|
||||
reject a valid one (over-reject). This asymmetry drives every decision below.
|
||||
|
||||
---
|
||||
|
||||
## 1. What PureXML is
|
||||
|
||||
- A **pure-Swift** XML parser, emitter, and **XSD 1.0 schema validator**, with
|
||||
XPath/XSLT support. It is a standalone Swift package (root `Package.swift`),
|
||||
not part of a larger workspace.
|
||||
- It compiles a `<xs:schema>` document into an internal model and then validates
|
||||
XML instances against it.
|
||||
- The active work is **schema-validity conformance**: making the validator
|
||||
correctly *reject* invalid schemas (and accept valid ones), measured against
|
||||
the W3C XML Schema Test Suite (XSTS).
|
||||
|
||||
### Non-negotiable constraints (these are hard rules, never violate them)
|
||||
|
||||
1. **No external SwiftPM dependencies.** Do not add a package dependency. Ever.
|
||||
2. **No C, C++, Objective-C, JavaScript, or any generated parser runtime** in the
|
||||
package. Pure Swift only.
|
||||
3. **Public API lives under the `PureXML` namespace tree.** Do not add public
|
||||
symbols outside it.
|
||||
4. **Must build on macOS, Linux, and WASI.** Do not use APIs unavailable on
|
||||
Linux/WASI. Foundation is allowed only for file access in tests.
|
||||
5. **Clean-room only.** There are reference implementations (Xerces, libxml2,
|
||||
.NET, Python `xmlschema`) under a separate research repo. You may **read them
|
||||
to understand a rule**, but you must **never copy their code**. Re-derive in
|
||||
Swift.
|
||||
6. **Tests may import only** `Foundation`, `Testing`, and `@testable import
|
||||
PureXML`. Never `import PureXML` (use `@testable`). Never import anything else
|
||||
in a test.
|
||||
|
||||
---
|
||||
|
||||
## 2. Repository layout (where things are)
|
||||
|
||||
- `Sources/Schema/`, the XSD schema engine. **Almost all your work is here.**
|
||||
- `Sources/` (other folders), the XML parser, emitter, XPath, XSLT, regex.
|
||||
- `Tests/`, Swift Testing test suites. One file per topic.
|
||||
- `Tests/XSTSSuiteTests.swift`, the opt-in conformance runner (Section 5).
|
||||
- `docs/`, rules and design docs. **Read these:**
|
||||
- `docs/production-readiness.md`, the bar for shipping. Read it every time.
|
||||
- `docs/schema-validity-burndown.md`, the conformance plan, priority order,
|
||||
and the per-change protocol. **This is your playbook.**
|
||||
- `docs/rules/code-style.md`, `namespacing.md`, `cross-platform.md`,
|
||||
`testing.md`, `verification.md`, `research-first.md`, coding rules.
|
||||
- `docs/xsts-deviations.md`, documented, intentional deviations (do not
|
||||
"fix" these; they are spec ambiguities).
|
||||
- `CHANGELOG.md`, every shippable change gets an entry here.
|
||||
- `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, agent instruction files (read first).
|
||||
|
||||
---
|
||||
|
||||
## 3. Environment and tools
|
||||
|
||||
You need these on the machine (they are already installed in the normal dev
|
||||
environment):
|
||||
|
||||
- `swift` (Swift 6.1+ toolchain).
|
||||
- `swiftformat` and `swiftlint` (formatting/lint gates).
|
||||
- The XSTS corpus, unpacked at: **`/private/tmp/xsts/xmlschema2006-11-06`**.
|
||||
This path is the value of the `XSTS_ROOT` environment variable used by the
|
||||
conformance runner. If it is missing, the conformance runner silently does
|
||||
nothing (it is opt-in), so confirm the directory exists before relying on it:
|
||||
`ls /private/tmp/xsts/xmlschema2006-11-06/suite.xml`.
|
||||
|
||||
---
|
||||
|
||||
## 4. The commands (run these, in this order, for every change)
|
||||
|
||||
These are the project's gates. A change is not done until **all** pass.
|
||||
|
||||
```sh
|
||||
# 1. Build.
|
||||
swift build
|
||||
|
||||
# 2. Format check, then lint (strict). Fix any violation before continuing.
|
||||
swiftformat . --config .swiftformat --lint
|
||||
swiftlint --config .swiftlint.yml --strict
|
||||
|
||||
# 3. Project style/namespacing checks.
|
||||
bash scripts/check-style.sh
|
||||
bash scripts/check-namespacing.sh
|
||||
|
||||
# 4. Full unit test suite (fast; excludes the opt-in XSTS runner).
|
||||
swift test
|
||||
|
||||
# 5. WASI build check (cross-platform gate).
|
||||
bash scripts/check-wasm.sh
|
||||
|
||||
# 6. The conformance differential (opt-in; ~13 seconds in release).
|
||||
XSTS_ROOT=/private/tmp/xsts/xmlschema2006-11-06 swift test -c release --filter XSTS
|
||||
```
|
||||
|
||||
If `swiftformat --lint` reports a file needs formatting, run
|
||||
`swiftformat . --config .swiftformat` (no `--lint`) to fix it, then re-run the
|
||||
lint. **Always format new files before committing**, the pre-push hook will
|
||||
otherwise reject the push.
|
||||
|
||||
---
|
||||
|
||||
## 5. The conformance campaign (what success is measured by)
|
||||
|
||||
The W3C XSTS runner (`Tests/XSTSSuiteTests.swift`) runs the entire suite and
|
||||
counts four buckets. The expected counts are **pinned** as constants near the
|
||||
top of that file:
|
||||
|
||||
| Constant | Meaning | Current value | Direction |
|
||||
|---|---|---|---|
|
||||
| `knownSchemaValidRejected` | valid schemas we wrongly **reject** | **1** | must NEVER rise; drive toward 0 |
|
||||
| `knownSchemaInvalidAccepted` | invalid schemas we wrongly **accept** | **394** | drive DOWN |
|
||||
| `knownInstanceValidRejected` | valid instances we wrongly **reject** | **180** | must not rise; drive toward 0 |
|
||||
| `knownInstanceInvalidAccepted`| invalid instances we wrongly accept | **160→158** (158) | drive down |
|
||||
|
||||
The runner asserts these counts **exactly**. So:
|
||||
|
||||
- When your change correctly rejects N more invalid schemas, the actual
|
||||
`schemaInvalidAccepted` drops by N and the test "fails" reporting the new
|
||||
lower number. That is success, **update the pinned constant to the new
|
||||
number**.
|
||||
- If **any** bucket goes UP, that is a regression. **Stop. Revert or fix.** A
|
||||
rise in `schemaValidRejected` or `instanceValidRejected` (a false positive) is
|
||||
the worst case and is never acceptable.
|
||||
|
||||
The per-case failures are written to **`/tmp/xsts-failures.txt`** after each
|
||||
run, one line per disagreement, e.g. `name00504m2: invalid schema accepted`. Use
|
||||
this file to find and classify work (Section 7).
|
||||
|
||||
> **Caution:** `/tmp/xsts-failures.txt` is written only *after* the run
|
||||
> completes. If the run crashes, the file is **stale**. Always check the exit
|
||||
> code and the printed bucket line, not just the file.
|
||||
|
||||
The production bar (read `docs/production-readiness.md`): all four buckets to 0
|
||||
in both directions; false positives (valid rejected) are the worst stopper;
|
||||
disclosed under-rejection is acceptable debt, hidden debt is a violation.
|
||||
|
||||
---
|
||||
|
||||
## 6. The per-change procedure (the "PR-critic-loop"), follow every step
|
||||
|
||||
Do **one** schema-validity rule per change. For each one:
|
||||
|
||||
1. **Measure first.** Look at `/tmp/xsts-failures.txt`. Pick a family of related
|
||||
failing cases (Section 7). **Read the actual `.xsd` files** of several cases
|
||||
in that family before writing any code, do not assume what rule they need.
|
||||
Confirm they all fail for the *same* reason.
|
||||
|
||||
2. **State the rule in one sentence**, citing the XSD 1.0 clause it enforces. If
|
||||
you are unsure of the spec, read `docs/rules/research-first.md` and consult
|
||||
the reference sources (study only, never copy).
|
||||
|
||||
3. **Implement at the compile-time consistency layer.** New schema-validity
|
||||
checks live in `Sources/Schema/` and return `[String]` (a list of error
|
||||
messages; empty = valid). They are wired into one of the aggregators (see
|
||||
Section 9). Keep the check **conservative**: only reject when you are certain
|
||||
the schema is invalid.
|
||||
|
||||
4. **Add unit tests** in a new `Tests/Schema*Tests.swift` file: at least one
|
||||
schema that must be **rejected** and several valid variants that must
|
||||
**compile**. Write the test file **before** running the conformance
|
||||
differential (see the race note in Section 8).
|
||||
|
||||
5. **Run all gates** (Section 4). Build, format, lint, unit tests must be green.
|
||||
|
||||
6. **Run the conformance differential** (Section 4, step 6). Confirm:
|
||||
`schemaInvalidAccepted` went DOWN, and **no other bucket went up** (this is a
|
||||
hard gate). If `schemaValidRejected` rose, you introduced a false positive ,
|
||||
find which valid schema you broke (diff `/tmp/xsts-failures.txt`), and fix or
|
||||
revert.
|
||||
|
||||
7. **Run an adversarial critic** (Section 6a). This is mandatory.
|
||||
|
||||
8. **Update the pinned baseline** constant in `Tests/XSTSSuiteTests.swift` to the
|
||||
new lower number. Re-run the conformance differential to confirm it now
|
||||
passes.
|
||||
|
||||
9. **Document.** Add a `CHANGELOG.md` entry (under `## [Unreleased]`, in the
|
||||
`### Fixed` or `### Added` section) describing the rule, the clause, the XSTS
|
||||
delta, and any disclosed under-rejection. Update the count in
|
||||
`docs/schema-validity-burndown.md`.
|
||||
|
||||
10. **Commit and merge** (Section 10).
|
||||
|
||||
### 6a. The adversarial critic (mandatory, do not skip)
|
||||
|
||||
Before merging, spawn a **separate** review pass whose *only job is to find a
|
||||
valid schema your new check would wrongly reject.* Give it:
|
||||
|
||||
- the exact files you changed,
|
||||
- the rule and the XSD clause,
|
||||
- an instruction to **construct concrete valid `<xs:schema>` documents** that
|
||||
the new code would reject, build them, and run them against the compiler to
|
||||
confirm.
|
||||
|
||||
The critic must specifically probe the **namespace-conflation trap** (Section
|
||||
8): does the rule confuse a built-in or imported type/element/attribute with a
|
||||
same-local-name user one? If the critic finds a real false positive, **fix it
|
||||
before merging** (usually by namespace-gating, Section 8). The XSTS suite does
|
||||
**not** contain every adversarial case, so the critic catches false positives
|
||||
the suite misses. This has happened repeatedly; it is the single highest-value
|
||||
safety step.
|
||||
|
||||
---
|
||||
|
||||
## 7. How to pick the next task (measure-first)
|
||||
|
||||
```sh
|
||||
# Group the current failing invalid-schema cases by name family:
|
||||
grep 'invalid schema accepted' /tmp/xsts-failures.txt \
|
||||
| sed -E 's/[0-9].*//; s/_.*//; s/\/.*//' | sed -E 's/[0-9]+$//' \
|
||||
| sort | uniq -c | sort -rn | head -20
|
||||
```
|
||||
|
||||
Then for a chosen family, list its cases and **read the schemas**:
|
||||
|
||||
```sh
|
||||
grep 'invalid schema accepted' /tmp/xsts-failures.txt | grep -iE '^familyName'
|
||||
# find and read a case's .xsd:
|
||||
find /private/tmp/xsts/xmlschema2006-11-06 -iname 'caseName.xsd'
|
||||
```
|
||||
|
||||
**Estimate yield before investing.** Some families are large but each case is a
|
||||
different rule (e.g. `addB` one-offs) or needs a subproject (regex-engine
|
||||
fidelity, cross-document composition). Prefer a family where many cases share
|
||||
one clean, well-defined rule. A clean rule with low over-rejection risk and a
|
||||
yield of even 2 is fine; a risky rule (could reject valid schemas) is not worth
|
||||
it regardless of yield.
|
||||
|
||||
> **Watch out:** the same test-group name can appear in *different* testSets for
|
||||
> *different* rules (the failures file does not disambiguate). If a case seems
|
||||
> already handled, it may be a same-named case in another testSet. Verify by
|
||||
> reading the actual schema and finding its testSet.
|
||||
|
||||
---
|
||||
|
||||
## 8. Recurring traps and their exact fixes
|
||||
|
||||
These have each bitten this project multiple times. Memorize them.
|
||||
|
||||
### Trap A, the namespace-conflation trap (has occurred 7+ times)
|
||||
|
||||
**Symptom:** a new rule resolves a type/element/attribute name by stripping the
|
||||
prefix and matching the local name, and then wrongly rejects a valid schema
|
||||
because a **built-in** (e.g. `xs:string`) or an **imported** component shares
|
||||
that local name with a user component.
|
||||
|
||||
**Rule:** any check that resolves a schema name must **resolve its namespace**,
|
||||
not just strip the prefix. Use
|
||||
`PureXML.Schema.XSDNode.referenceNamespace(reference, bindings)` (where
|
||||
`bindings = XSDNode.namespaceBindings(of: schema)`) and only act when it resolves
|
||||
to the namespace you intend (usually the schema's own `targetNamespace`). A
|
||||
reference resolving to the XSD namespace is a built-in and must be skipped.
|
||||
|
||||
When a rule depends on cross-document resolution and the schema has
|
||||
`import`/`include`/`redefine`, the safest move is to **stand down entirely** for
|
||||
that schema (gate on `!hasExternalReference(schema)`), which is a disclosed
|
||||
under-rejection. Many existing checks do this.
|
||||
|
||||
### Trap B, swiftformat vs swiftlint brace conflict
|
||||
|
||||
**Symptom:** you write a multi-condition `if a, b, c {` (or `for ... where ... {`)
|
||||
that is long; swiftformat moves the `{` to its own line; swiftlint then reports
|
||||
an `opening_brace` violation. The two tools disagree and you cannot satisfy both
|
||||
with that shape.
|
||||
|
||||
**Fix:** extract the condition into a `Bool`-returning helper so the `if` is a
|
||||
single short expression with the brace on the same line:
|
||||
|
||||
```swift
|
||||
if isNestedNamedDefinition(node, kind, parentIsTopLevel: parentIsTopLevel) {
|
||||
errors.append(...)
|
||||
}
|
||||
```
|
||||
|
||||
For `for ... where`, convert a long `where` into a `guard ... else { continue }`
|
||||
inside the loop body so the `for` line stays short.
|
||||
|
||||
### Trap C, file/function size limits
|
||||
|
||||
swiftlint enforces: **file ≤ 400 lines**, **type body ≤ 250 lines**, **function
|
||||
cyclomatic complexity ≤ 10**, **function parameters ≤ 5**, **line ≤ 180 chars**.
|
||||
When you hit one:
|
||||
|
||||
- File too long → extract the new check into its **own new file**
|
||||
(`Sources/Schema/SchemaYourRule.swift`), `extension PureXML.Schema.XSDParser`.
|
||||
- Complexity > 10 → split the function into smaller helpers.
|
||||
- Params > 5 → bundle related arguments into one labeled tuple (≤ 2 members).
|
||||
- A long `[String]` concatenation can time out the type-checker → break it into
|
||||
intermediate `let` bindings.
|
||||
|
||||
### Trap D, the test-write-before-differential race
|
||||
|
||||
If you launch the (release) conformance build/run and *then* create a new test
|
||||
file, the release build may pick up a half-written file and fail with confusing
|
||||
empty output. **Always write and save the new test file first**, run the fast
|
||||
`swift test`, and only then launch the release XSTS differential.
|
||||
|
||||
### Trap E, reading schema attributes by local name
|
||||
|
||||
`XSDNode.attribute(node, "name")` matches by **local name only**. For a
|
||||
schema-vocabulary attribute (`name`, `use`, `form`, `ref`, `default`, `fixed`,
|
||||
…) read the **unprefixed** one (`attribute.name.prefix == nil`) so a foreign
|
||||
`pre:name` is not mistaken for the real attribute. Skip foreign-namespace nodes
|
||||
(`node.name?.namespaceURI == xsdNamespace`) and never descend into
|
||||
`xs:appinfo` / `xs:documentation` (foreign content).
|
||||
|
||||
---
|
||||
|
||||
## 9. Where the checks live (file map)
|
||||
|
||||
Schema-validity checks are aggregated in two places, both in
|
||||
`Sources/Schema/SchemaIDAttribute.swift`:
|
||||
|
||||
- **`consistencyErrors(schema, context, containers)`**, checks that run before
|
||||
the named types are compiled (structural / id / determinism / cycles /
|
||||
placement). Structural checks are gathered by `structureErrors(schema)` in
|
||||
`Sources/Schema/SchemaStructure.swift`.
|
||||
- **`postNamedTypeErrors(schema, context, containers, derivation, typeMaps)`** ,
|
||||
checks that need the **compiled** types (`typeMaps.named` is the
|
||||
`[String: ElementType]` map): references, attribute-use uniqueness, value
|
||||
constraints against user types, substitution-member type derivation, all-group
|
||||
in extension.
|
||||
|
||||
To add a new check: write a `static func yourRuleErrors(...) -> [String]` in a
|
||||
new file, then add `+ yourRuleErrors(...)` to the appropriate aggregator. Use
|
||||
compiled types (`postNamedTypeErrors`) only if your rule needs resolved type
|
||||
content; otherwise prefer the structural layer.
|
||||
|
||||
Representative existing checks to imitate (read these as templates):
|
||||
`SchemaAttributeApplicability.swift` (per-node + top-level scans),
|
||||
`SchemaNestedDefinition.swift` (recursive top-level/nested walk),
|
||||
`SchemaSimpleTypeFinal.swift` (namespace-gated name map),
|
||||
`SchemaExtensionAllGroup.swift` (compiled-type lookup),
|
||||
`SchemaFacetApplicability.swift` (value/facet checks),
|
||||
`XSDSimpleParser.swift` (`facetDefinitionErrors`).
|
||||
|
||||
---
|
||||
|
||||
## 10. Commit, branch, and release discipline (follow exactly)
|
||||
|
||||
- **Branch off `main`** for each change: `git checkout -b schema/your-rule-name`.
|
||||
- Commit message format: `type(scope): summary`, e.g.
|
||||
`feat(schema): reject X (cos-rule-name)`. Body explains the rule, the clause,
|
||||
and the XSTS delta.
|
||||
- **Never use an em dash** anywhere in commit messages, code, comments, or docs.
|
||||
Use a comma, period, colon, or rewrite the sentence.
|
||||
- **Never add AI attribution** of any kind, no "Generated with", no
|
||||
"Co-Authored-By", no mention of Gemini/Antigravity/AI/assistant/"agent" in
|
||||
commit messages, PR descriptions, branch names, code comments, or any output
|
||||
that lands in the repo. All work must read as human-authored. (This document
|
||||
is the one exception, because it was explicitly requested as agent
|
||||
onboarding.)
|
||||
- **Never `git push` to a GitLab remote.** Only GitHub remotes are allowed.
|
||||
Inspect the remote URL before pushing; if it contains `gitlab`, stop.
|
||||
- Merge to `main` with a fast-forward only: `git checkout main && git merge
|
||||
--ff-only your-branch`, then `git push origin main`. Delete the branch.
|
||||
- A pre-push hook runs `swift test` and a commit-message style check; if the
|
||||
push is rejected for formatting, format the offending file, amend, and push
|
||||
again.
|
||||
- **Tagged releases:** bump `CHANGELOG.md` (move `## [Unreleased]` content under
|
||||
a new `## [x.y.z] - YYYY-MM-DD` header, add a fresh empty `[Unreleased]`),
|
||||
commit, then `git tag -a vX.Y.Z -m "..."`, `git push origin vX.Y.Z`, and
|
||||
`gh release create vX.Y.Z --title ... --notes ...`. The project is pre-1.0;
|
||||
stay on `0.x` until `docs/production-readiness.md`'s gates are met.
|
||||
|
||||
---
|
||||
|
||||
## 11. Current state (snapshot at v0.1.0)
|
||||
|
||||
- Pinned baselines: **valid-schemas-rejected 1**, **invalid-schemas-accepted
|
||||
394**, **valid-instances-rejected 180**, **invalid-instances-accepted 158**.
|
||||
- The clean structural / applicability rule families are largely **done**:
|
||||
facet validity, structural content-model, UPA determinism, content order,
|
||||
identity-constraint XPath subset, wildcard namespace, final/block value space,
|
||||
simpleType content, circular derivation/references, attribute-use uniqueness,
|
||||
type-excludes-inline, ID value-constraint, all-group placement, element-ref
|
||||
namespaces, particle-restriction (MapAndSum etc.), substitution-member type,
|
||||
default/fixed exclusion, default/use, value-against-user-type, top-level and
|
||||
ref applicability, whiteSpace restriction direction, simpleType final
|
||||
list/union, all-group-in-extension, nested-named-definition.
|
||||
- **What remains (each is a deliberate subproject, not a one-line rule):**
|
||||
- **Regex-engine fidelity** (`RegexTest` family, ~22): make the pattern
|
||||
compiler correctly reject genuinely-invalid patterns *without* rejecting
|
||||
valid ones. **High over-rejection risk**, be very careful.
|
||||
- **Particle-restriction tail** (`particlesIk/L/Z/Hb/Ig`, ~40): needs the
|
||||
particle model enriched with more type information (NameAndTypeOK clauses
|
||||
nillable/fixed/block; Particle-Valid-Extension).
|
||||
- **Cross-document composition** (`attg`/`schZ`/`schN`, ~50): load and merge
|
||||
`import`/`include`/`redefine` so the existing rules run over the merged
|
||||
schema. This unlocks the largest remaining cluster.
|
||||
- **`addB`** (~19): adhoc one-offs, each its own investigation.
|
||||
- **Located diagnostics** (`#169`): line/column positions on every error (the
|
||||
IDE gate; a broad but mechanical refactor of the `[String]` error paths to
|
||||
carry `SourceRange`).
|
||||
|
||||
Pick from these only as deliberate, well-scoped efforts. Do not start one you
|
||||
cannot finish cleanly. If you cannot find a clean, low-risk rule, say so rather
|
||||
than forcing a risky change.
|
||||
|
||||
---
|
||||
|
||||
## 12. A worked example (one complete iteration)
|
||||
|
||||
The most recent change (`nested-named-definition`) is a model iteration:
|
||||
|
||||
1. Measured `/tmp/xsts-failures.txt`; saw `stA*` and `attgB*` families.
|
||||
2. Read `stA008.xsd` (a `<simpleType name="foo">` nested inside a
|
||||
`<restriction>`) and `attgB002.xsd` (a `<attributeGroup name="abc">` nested
|
||||
inside another). Rule: a nested `simpleType`/`attributeGroup` may not be named
|
||||
(schema-for-schemas `localSimpleType` / nested `attributeGroup` is a ref).
|
||||
3. Wrote `Sources/Schema/SchemaNestedDefinition.swift`: a recursive walk passing
|
||||
`parentIsTopLevel`, flagging a non-top-level XSD-namespace
|
||||
`simpleType`/`attributeGroup` with an unprefixed `name`; skipping
|
||||
appinfo/documentation. Wired into `structureErrors`.
|
||||
4. Wrote `Tests/SchemaNestedDefinitionTests.swift` (reject nested-named; accept
|
||||
nested-anonymous, attributeGroup ref, top-level named).
|
||||
5. Ran gates → green. Hit Trap B (brace conflict) → extracted a `Bool` helper.
|
||||
6. Ran the differential → `invalid-schemas-accepted` 403 → 394, all other
|
||||
buckets held. Updated the pinned baseline to 394.
|
||||
7. Ran the critic (17 constructed valid schemas) → zero false positives.
|
||||
8. CHANGELOG entry, burndown count, commit on a branch, `--ff-only` merge, push.
|
||||
|
||||
Total: one clean, verified, reversible improvement, with the false-positive line
|
||||
held at zero.
|
||||
|
||||
---
|
||||
|
||||
## 13. Definition of done (self-audit before you say "done")
|
||||
|
||||
- [ ] `swift build` succeeds.
|
||||
- [ ] `swiftformat --lint` and `swiftlint --strict` report zero violations.
|
||||
- [ ] `bash scripts/check-style.sh`, `check-namespacing.sh`, `check-wasm.sh` pass.
|
||||
- [ ] `swift test` is fully green (all unit tests).
|
||||
- [ ] The XSTS differential shows `invalid-schemas-accepted` down and **no other
|
||||
bucket up** (`valid-schemas-rejected` still 1).
|
||||
- [ ] An adversarial critic found **zero** valid schemas your change rejects.
|
||||
- [ ] New unit tests cover both a rejected case and several accepted cases.
|
||||
- [ ] The pinned baseline in `Tests/XSTSSuiteTests.swift` matches the new count.
|
||||
- [ ] `CHANGELOG.md` and `docs/schema-validity-burndown.md` updated.
|
||||
- [ ] Commit message has the right format, no em dash, no AI attribution.
|
||||
- [ ] Merged `--ff-only` to `main`; pushed to the GitHub remote (never GitLab).
|
||||
|
||||
If any box is unchecked, you are not done. Ask: "did I take the optimal path, or
|
||||
one I hope no one inspects?" If you wrongly rejected even one valid schema, the
|
||||
change is wrong no matter how many invalid ones it caught.
|
||||
Loading…
Reference in New Issue
Block a user