Skip to content

perf(schema): validate each intent graph object once (ticket-109) - #140

Merged
ifuri-validator-agent[bot] merged 1 commit into
mainfrom
ticket/109-validate-once
Oct 19, 2026
Merged

ifuri-validator-agent[bot] merged 1 commit into
mainfrom
ticket/109-validate-once

Conversation

@tom-sapletta-com

@tom-sapletta-com tom-sapletta-com commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Closes #135.

assertIntentGraph used to re-validate the same graph object about 9 times per pipeline and recompute its canonical-JSON fingerprint each time. A WeakMap memo now skips a repeated full validation when the object still has:

  • the same fingerprint string;
  • the same records / relations array identities and lengths.

Failed validations are never cached, and a revert restores the old behaviour.

Measured (superseded: see the final comment, where 3 interleaved CPU-time pairs give 360.7 → 255.3 s, 1.41×) on the same build, compare-workspace on subactor/platform (50k records / 415k relations):

wall peak RSS
T2C_REVALIDATE_GRAPHS=1 (old behaviour) 504 s 3.10 GB
memo (default) 249 s (single sample, not representative) 3.42 GB

The base and workspace fingerprints, trend and diff.summary are identical in both runs. The absolute times are inflated because another test suite was running on the machine at the same time; the A/B ratio is the meaningful figure.

Review note. Willman drafted the patch via koru.develop_ticket (glm-5.3) from an exact spec, and it passed review unchanged.

Verification:

  • npm run build && T2C_USE_SUBLLM=false node --test dist/test/schema-validate-once.test.js dist/test/schema-validation.test.js dist/test/structured-schema.test.js → 10 pass.
  • ./project/governance-check.sh --base origin/main → GOV-PASS.

Follow-ups: #136 (cache the base pipeline), #137 / #139 (exposed diagnostics), #138 (Rust kernel RFC).

🤖 Generated with Claude Code

@tom-sapletta-com

Copy link
Copy Markdown
Contributor Author

Correction to the 2.0× in the description. That came from one sample per side, taken while another heavy test suite was running, and wellmanifest/performance requires at least 3 comparable samples.

I re-ran it with 3 interleaved pairs (ABABAB) on the same build, using T2C_REVALIDATE_GRAPHS=1 as the old behaviour, with the host's load average between 26 and 53:

pair revalidate (old) memo (new) ratio
1 420.6 s 369.5 s 1.14
2 383.9 s 343.2 s 1.12
3 348.7 s 207.7 s 1.68

The median wall time drops from 384 s to 343 s, about 1.12×, but the spread is too large under this load to claim a precise figure. The output is identical in all 6 runs (base 5ac0a4e2…, workspace 01d6361d…).

I'm now measuring user+sys CPU time, which load affects less, and will attach the evidence and a performance.plan/v1 to the ticket once it finishes. The change still removes repeated full validations deterministically: the count goes from 18 to 2 per run.

@tom-sapletta-com
tom-sapletta-com force-pushed the ticket/109-validate-once branch from 5cd60af to 44e0b72 Compare October 19, 2026 18:53
@tom-sapletta-com

Copy link
Copy Markdown
Contributor Author

Final measurement and fixes (commit 44e0b72):

  • CI verify:env failure: fixed by removing the temporary T2C_REVALIDATE_GRAPHS switch, which wasn't declared in .env.example. Rollback is now a revert. Locally, make verify passes 458/459; the one failure is an environment-specific npm-output test (npm v12 on Node 20), unrelated to this change.
  • CPU-time A/B: 3 interleaved pairs with the same build and workload, user+sys CPU from /usr/bin/time:
CPU (median) samples
old (revalidate every call) 360.7 s 360.7, 373.7, 326.7
new (memo) 255.3 s 255.3, 300.4, 234.0

That is 1.41× less CPU; the per-pair ratios are 1.41, 1.24 and 1.40. Base and workspace fingerprints are identical across all 6 runs, and peak RSS is in the same range (2.9–3.5 GB).

  • Evidence: project/ticket-109/evidence/validate-once-ab.json.
  • Plan: project/ticket-109/performance-plan.json passes performance.py validate with ok.

This replaces both the earlier 2.0× claim and the wall-time comparison, which load made too noisy (see the previous comment).

 #135)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tom-sapletta-com

Copy link
Copy Markdown
Contributor Author

The koru / code-review failure was a real finding: the memo branches pushed assertIntentGraph to cyclomatic complexity 17, over the limit of 15 (complexity.lizard_cc). The check now lives in isUnchangedValidatedGraph / rememberValidatedGraph. lizard reports assertIntentGraph at 8 and the helper at 9; behaviour is unchanged and 10 schema tests pass. (The review's OpenRouter 401 is an advisory-only CI secret problem, not part of the verdict.)

@tom-sapletta-com
tom-sapletta-com force-pushed the ticket/109-validate-once branch from 44e0b72 to b7ded18 Compare October 19, 2026 20:06

@ifuri-validator-agent ifuri-validator-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Validator approval after policy checks for exact head b7ded180c86d011c14b5ade399d886a51fb0f6d6.

Ticket: ticket-109
Correlation ID: todo2code-pr-140-ticket-109-b7ded180c8
Model: zai/glm-5.3
Reviewed diff chunks: 2
Advisory LLM verdict: APPROVE
Advisory summary: Reviewed all 2 diff chunk(s). Chunk 1 of 2 contains documentation, ticket metadata, intent, and performance evidence for the validate-once memoization change. All required checks (verify, Java adapter, koru/code-review, governance/enforce) pass. Performance evidence is internally consistent (median CPU 360.7s -> 255.3s, identical fingerprints across arms, failures never cached per invariants). No security concerns in this chunk; implementation code is in chunk 2 and reviewed there. | The diff adds a WeakMap-based memo to assertIntentGraph that skips repeat full validation when the graph object retains the same fingerprint string, identical records/relations array identities, and matching lengths. Failed validations are never cached (rememberValidatedGraph only runs at the end), and the new tests cover the memo hit, cache miss on cloned objects, mutated fingerprint rejection, and forced revalidation on array replacement. All required checks (verify, Java adapter, koru / code-review, governance / enforce) pass; SKIPPING outcomes are observational checks only and do not block.
Advisory findings: none
The LLM output above is advisory and was not used as the approval trust root.
Semantic review prerequisite: not_required; policy 676cb4516bbfed2a000e40b9b1b6e4a430ecc761ec546aeb53d721a1905cfdd7.

Actual PR impact radar

Exact range: a85da84e72c6c3fc7d2ea1c8b6eaf790f972a63f...b7ded180c86d011c14b5ade399d886a51fb0f6d6
Change digest: f716f006b289746b2e22627e9e9fb1b31c03c32b5b6eb721e2170e62b0ca7610
Score: 80/100 (L), estimated 155 min, split recommended: true
Affected services/components: repository-wide/unclassified

Machine-readable radar JSONL and SVG
{"actual_change":{"additions":491,"base_sha":"a85da84e72c6c3fc7d2ea1c8b6eaf790f972a63f","binary_files":0,"categories":{"code":1,"configuration":3,"docs":2,"tests":1},"change_digest":"f716f006b289746b2e22627e9e9fb1b31c03c32b5b6eb721e2170e62b0ca7610","comparison":"a85da84e72c6c3fc7d2ea1c8b6eaf790f972a63f...b7ded180c86d011c14b5ade399d886a51fb0f6d6","deletions":0,"file_count":7,"files":["project/TICKETS.md","project/ticket-109/README.md","project/ticket-109/evidence/validate-once-ab.json","project/ticket-109/intent.json","project/ticket-109/performance-plan.json","src/core/schema-intent-validation.ts","test/schema-validate-once.test.ts"],"head_sha":"b7ded180c86d011c14b5ade399d886a51fb0f6d6","service_count":0,"services":[]},"assessment_mode":"observed-pr","axes":{"coupling":5,"delivery":4,"scope":5,"uncertainty":3,"validation":3},"complexity":"L","confidence":0.9,"diagnostics":["RADAR-ACCEPTANCE-MISSING","RADAR-BUDGET-EXCEEDED"],"estimate":{"budget_minutes":30,"minutes":155,"within_budget":false},"impact":{"components":["A","claude.com","dist","origin","project","src/core","subactor","test"],"files":["A/B","claude.com/claude-code","dist/test/schema-validate-once.test.js","dist/test/schema-validation.test.js","dist/test/structured-schema.test.js","origin/main","project/TICKETS.md","project/governance-check.sh","project/ticket-109/README.md","project/ticket-109/evidence/validate-once-ab.json","project/ticket-109/intent.json","project/ticket-109/performance-plan.json","src/core/schema-intent-validation.ts","subactor/platform","test/schema-validate-once.test.ts"],"public_interfaces":["dist/test/schema-validate-once.test.js","dist/test/schema-validation.test.js","dist/test/structured-schema.test.js","src/core/schema-intent-validation.ts","test/schema-validate-once.test.ts"],"runtime_dependencies":1},"schema":"subactor.ticket-radar/v1","score":80,"split":{"parts":[{"estimated_minutes":20,"name":"Define contract and acceptance boundary","scope":["dist/test/schema-validate-once.test.js","dist/test/schema-validation.test.js","dist/test/structured-schema.test.js","src/core/schema-intent-validation.ts","test/schema-validate-once.test.ts"]},{"estimated_minutes":16,"name":"Implement A","scope":["A"]},{"estimated_minutes":16,"name":"Implement claude.com","scope":["claude.com"]},{"estimated_minutes":16,"name":"Implement dist","scope":["dist"]},{"estimated_minutes":16,"name":"Implement origin","scope":["origin"]},{"estimated_minutes":16,"name":"Implement project","scope":["project"]},{"estimated_minutes":15,"name":"Validate and project to trackers","scope":["tests","planfile","github/gitlab/jira projections"]}],"reason":"estimated_minutes_exceed_budget","recommended":true},"standards":[{"id":"wellmanifest/dsl","revision":"6c60fc4e0dd1f1bb74f46a7745e28019908d1203","version":"0.1.0-dev"},{"id":"wellmanifest/ticket-lifecycle","revision":"5bf581907a87b46a13a73e6c033d3abe4d9a306f","version":"0.1.0-dev"},{"id":"wellmanifest/git-lifecycle","revision":"7d77d4b7af57e69bc75c3a0290b3a4805c5c4438","version":"0.2.0-dev"},{"id":"wellmanifest/logs","revision":"48c284ef7a069055c0bcb6b900147ce5e65f8b43","version":"0.3.0"}],"ticket_ref":"ticket-109"}
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img"><title>ticket-109: perf(schema): validate each intent graph object once (ticket-109)</title><rect width="128" height="128" rx="12" fill="#f8fafc"/><g stroke-width="1"><polygon points="64,55 72,61 69,71 59,71 56,61" fill="none" stroke="#d7dde5"/><polygon points="64,47 80,59 74,78 54,78 48,59" fill="none" stroke="#d7dde5"/><polygon points="64,38 89,56 79,85 49,85 39,56" fill="none" stroke="#d7dde5"/><polygon points="64,30 97,53 84,92 44,92 31,53" fill="none" stroke="#d7dde5"/><polygon points="64,21 105,51 89,99 39,99 23,51" fill="none" stroke="#d7dde5"/><line x1="64" y1="64" x2="64" y2="21" stroke="#aab4c0"/><line x1="64" y1="64" x2="105" y2="51" stroke="#aab4c0"/><line x1="64" y1="64" x2="89" y2="99" stroke="#aab4c0"/><line x1="64" y1="64" x2="39" y2="99" stroke="#aab4c0"/><line x1="64" y1="64" x2="23" y2="51" stroke="#aab4c0"/></g><polygon points="64,21 105,51 79,85 49,85 31,53" fill="#fb923c" fill-opacity="0.45" stroke="#c2410c" stroke-width="2"/><circle cx="64" cy="64" r="3" fill="#c2410c"/><g font-family="sans-serif" font-size="7" fill="#334155"><text x="64" y="11" text-anchor="middle">SCO</text><text x="114" y="48" text-anchor="middle">COU</text><text x="95" y="107" text-anchor="middle">UNC</text><text x="33" y="107" text-anchor="middle">VAL</text><text x="14" y="48" text-anchor="middle">DEL</text></g><text x="64" y="124" text-anchor="middle" font-family="sans-serif" font-size="8" fill="#0f172a">L · 155m</text></svg>
Merge will be attempted after this approval when explicitly authorized. ## Decision record (recomputable)
DECISION D-109-9992
TICKET ticket-109
HEAD_SHA b7ded180c86d011c14b5ade399d886a51fb0f6d6
CORRELATION_ID todo2code-pr-140-ticket-109-b7ded180c8
ACTOR agent:ifuri-validator-agent[bot]
APPLIED_RULE P-CORE-015
INPUT author_login = "tom-sapletta-com"
INPUT observed_checks = ["Live OpenRouter contract (opt-in)=SKIPPING","governance=SKIPPING","verify=PASS","Java adapter (JDK 17 required)=PASS","governance / remote lifecycle=PASS","koru / code-review=PASS","governance / enforce=PASS","verify=PASS","Java adapter (JDK 17 required)=PASS"]
INPUT required_checks = ["verify","Java adapter (JDK 17 required)","koru / code-review","governance / enforce","governance / governance / enforce"]
INPUT required_checks_source = "protected registry + GitHub applied rules (env/request)"
INPUT reviewer_login = "ifuri-validator-agent[bot]"
INPUT semantic_review_assessment = {"schema":"subactor.validator/semantic-review-assessment/v1","subject":{"repository":"autogrammar/todo2code","pull_request":140,"head_sha":"b7ded180c86d011c14b5ade399d886a51fb0f6d6","base_sha":"a85da84e72c6c3fc7d2ea1c8b6eaf790f972a63f","diff_sha256":"5b8dbc9b1b2eb17b8cf03c5545c9a41b4e23a56bae9fef1a05e48da1a7eb31bc"},"policy":{"policy_schema":"subactor.validator/semantic-review-policy/v1","policy_version":1,"policy_sha256":"676cb4516bbfed2a000e40b9b1b6e4a430ecc761ec546aeb53d721a1905cfdd7","required":false,"critical_paths":[],"observed_paths":["project/TICKETS.md","project/ticket-109/README.md","project/ticket-109/evidence/validate-once-ab.json","project/ticket-109/intent.json","project/ticket-109/performance-plan.json","src/core/schema-intent-validation.ts","test/schema-validate-once.test.ts"]},"grounding":"full-diff-not-per-finding-proof","execution_authority":false,"status":"not_required","reason":null,"review_sha256":null,"unresolved":[]}
INPUT superseded_checks = []
INPUT ticket_radar_receipt = {"schema":"subactor.ticket-radar/v1","base_sha":"a85da84e72c6c3fc7d2ea1c8b6eaf790f972a63f","head_sha":"b7ded180c86d011c14b5ade399d886a51fb0f6d6","change_digest":"f716f006b289746b2e22627e9e9fb1b31c03c32b5b6eb721e2170e62b0ca7610","score":80,"complexity":"L","estimated_minutes":155,"split_recommended":true,"services":[],"authority":"ADVISORY","promotion":"FORBIDDEN"}
VERDICT APPROVE AUTHORITY DETERMINISTIC
REJECTED REQUEST_CHANGES BECAUSE NO_UNSAFE_CHANGE_REASON_FOUND
ADVISORY llm_verdict = "APPROVE" MODEL "zai/glm-5.3"
ASSERT VERDICT_AUTHORITY != "ADVISORY"

@ifuri-validator-agent
ifuri-validator-agent Bot merged commit fd4f81a into main Oct 19, 2026
15 checks passed
@ifuri-validator-agent
ifuri-validator-agent Bot deleted the ticket/109-validate-once branch October 19, 2026 22:38
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.

perf: compare-workspace validates each graph ~9x and fingerprints it ~10x (160 s of a 290 s run)

1 participant