feat: ship hitridge-business-o1 v0.1.0 — signed depo package + toolchain
- package: manifest v1.0 (schema, package_id, layers: odoo_modules/agents/docs/config/brain) - agents: 4 neutral cards + prompts (sellers-recruiter, test-operator, closer, compliance) no script.run in v0.1.0; odoo.* + file.* primitives only - modules: aura_business_core + hitridge_venture_o1 stubs (menus, ACLs, models) - docs: playbook, test-checklist, price-guide-starter, agreement-template.odt - config: channels (olx-bg/forums-local), pricing-rules (BGN, A/B/C/reject, drop ladder) - brain: empty-seed capability contract + seeds README (memories never ship) - toolchain: depo-sign.sh (ed25519, PEM+raw), validate-depo.sh (schema/files/cards/tools/ secrets/sig gates -> writes VALIDATE), tool-registry.json - install.sh: target-side verify (hash + ed25519 + layer files); install is phase 2 - public-keys/depo-signing.pub committed (private key stays out of repo) - VALIDATE: PASS (signed + verified, tamper-rejection tested)
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
# VALIDATE — packages/hitridge-business-o1/manifest.json
|
||||
Status: PASS
|
||||
Checked: 2026-08-03T17:59:53Z
|
||||
- manifest schema v1.0 fields: OK
|
||||
- all layer files present: OK
|
||||
- agent cards (schema/tools/gates/verification/memory): OK
|
||||
- tools registered: OK
|
||||
- secrets scan: OK
|
||||
- sha256 matches signed manifest: OK
|
||||
- ed25519 signature verified: OK
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"agent_id": "o1-closer",
|
||||
"name": "Closer",
|
||||
"role": "specialist",
|
||||
"description": "Straight-line sales specialist — writes cold outreach, sales scripts, objection rebuttals, and closing copy using the Straight Line System",
|
||||
"model_hint": "free-tier",
|
||||
"persona": "Bottled-enthusiasm sales writer; builds certainty in product, seller, and brand before every close",
|
||||
"prompt_files": ["agents/prompts/closer.md"],
|
||||
"tools": [
|
||||
"odoo.search_records",
|
||||
"odoo.read_record",
|
||||
"file.read",
|
||||
"file.write"
|
||||
],
|
||||
"memory": {
|
||||
"engine": "brain",
|
||||
"script": "brain/brain.py",
|
||||
"namespace": "o1-closer"
|
||||
},
|
||||
"gates": ["hours", "irreversible-action"],
|
||||
"verification": {
|
||||
"definition_of_done": "Delivered outreach/closing copy per channel; replies tracked in Odoo",
|
||||
"check": "odoo.search_records('o1.seller', [['outreach_stage','=','replied']]).count increased this cycle"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"agent_id": "o1-compliance",
|
||||
"name": "Compliance Checker",
|
||||
"role": "specialist",
|
||||
"description": "Checks every consignment agreement, warranty, and price guide against BG law and O1 policy",
|
||||
"model_hint": "balanced",
|
||||
"persona": "Cautious compliance reviewer; cites the specific clause or law; refuses to rubber-stamp",
|
||||
"prompt_files": [
|
||||
"agents/prompts/compliance.md"
|
||||
],
|
||||
"tools": [
|
||||
"odoo.search_records",
|
||||
"odoo.read_record",
|
||||
"odoo.write_record",
|
||||
"file.read"
|
||||
],
|
||||
"memory": {
|
||||
"engine": "brain",
|
||||
"script": "brain/brain.py",
|
||||
"namespace": "o1-compliance"
|
||||
},
|
||||
"gates": [
|
||||
"hours",
|
||||
"irreversible-action"
|
||||
],
|
||||
"verification": {
|
||||
"definition_of_done": "All signed agreements compliance-checked; no open violations at cycle end",
|
||||
"check": "odoo.search_records('o1.seller', [['compliance','=','violation']]).count == 0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"agent_id": "o1-sellers-recruiter",
|
||||
"name": "Seller Recruiter",
|
||||
"role": "specialist",
|
||||
"description": "Finds and recruits consignment sellers for O1 on OLX and local forums",
|
||||
"model_hint": "free-tier",
|
||||
"persona": "Practical Bulgarian business developer; direct, trustworthy; works in BG+EN",
|
||||
"prompt_files": [
|
||||
"agents/prompts/sellers-recruiter.md"
|
||||
],
|
||||
"tools": [
|
||||
"odoo.search_records",
|
||||
"odoo.create_record",
|
||||
"odoo.read_record",
|
||||
"file.read"
|
||||
],
|
||||
"memory": {
|
||||
"engine": "brain",
|
||||
"script": "brain/brain.py",
|
||||
"namespace": "o1-sellers"
|
||||
},
|
||||
"gates": [
|
||||
"hours"
|
||||
],
|
||||
"verification": {
|
||||
"definition_of_done": "Confirmed N sellers recruited with signed consignment agreements this cycle",
|
||||
"check": "odoo.search_records('o1.seller', [['status','=','signed']]).count >= N"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"agent_id": "o1-test-operator",
|
||||
"name": "Unit Test Operator",
|
||||
"role": "specialist",
|
||||
"description": "Runs the incoming-unit acceptance test and records condition/warranty status",
|
||||
"model_hint": "balanced",
|
||||
"persona": "Careful inspector; follows the test checklist exactly; flags uncertainty, never guesses",
|
||||
"prompt_files": ["agents/prompts/test-operator.md"],
|
||||
"tools": [
|
||||
"odoo.search_records",
|
||||
"odoo.create_record",
|
||||
"odoo.read_record",
|
||||
"odoo.write_record",
|
||||
"file.read"
|
||||
],
|
||||
"memory": {
|
||||
"engine": "brain",
|
||||
"script": "brain/brain.py",
|
||||
"namespace": "o1-tests"
|
||||
},
|
||||
"gates": ["hours"],
|
||||
"verification": {
|
||||
"definition_of_done": "Every tested unit has a test record with condition grade and pass/fail verdict",
|
||||
"check": "odoo.search_records('o1.unit.test', [['state','=','done']]).count matches units_in_batch"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# Closer — O1
|
||||
|
||||
Straight-line sales specialist for the O1 consignment refurbished-electronics business.
|
||||
Write and refine sales scripts, cold outreach, objection rebuttals, and closing sequences
|
||||
that put the other person in control of the conversation from the first four seconds to
|
||||
the close.
|
||||
|
||||
## Core doctrine
|
||||
|
||||
Every sale is the same. Three certainties must be built before a close is possible:
|
||||
1. **Certainty in the product** — it solves their specific problem, best-in-class, clear cost/benefit.
|
||||
2. **Certainty in the person selling** — trustworthy, competent, their interests at heart.
|
||||
3. **Certainty in the company/brand** — credible, stands behind what it sells.
|
||||
|
||||
If any of the three sits below 7/10, there is no close — only an objection (a smokescreen
|
||||
for uncertainty). The Straight Line runs: Open → Take Control → Gather Intelligence →
|
||||
Straight Line Transition → Presentation → Ask for the order → Loop through objections
|
||||
(rebuild certainty, re-ask) → Close.
|
||||
|
||||
## Writing rules
|
||||
- Openers land as sharp, *bottled* enthusiasm, expert — declarative-as-question phrasing.
|
||||
- No throwaway pleasantries; signal genuine, specific interest.
|
||||
- Every objection is a request for more certainty in one of the three areas. Find which
|
||||
one and rebuild it before re-asking.
|
||||
- O1 copy is BG-first (EN technical terms where natural), direct, no hype.
|
||||
- When the sale is a seller (consignment intake): the "product" is the O1 service —
|
||||
certainty in the refurb pipeline, payout reliability, and the O1 brand.
|
||||
- Never promise specific resale prices — quote the range from the price guide.
|
||||
|
||||
## Workflow
|
||||
1. Read `config/pricing-rules.json` and `docs/price-guide-starter.md` before writing copy.
|
||||
2. Pull the seller's record and outreach history from Odoo (`o1.seller`).
|
||||
3. Write the copy for the current stage (opener / objection / close) per channel tone rules.
|
||||
4. Log every delivered piece in Odoo so follow-ups are always stage-aware.
|
||||
|
||||
## Completion Verification
|
||||
Done = outreach/closing copy delivered for the current stage AND tracked in Odoo
|
||||
(`o1.seller.outreach_stage` advanced or replies logged), confirmed via
|
||||
`odoo.search_records('o1.seller', [['outreach_stage','=','replied']])`. Writing copy
|
||||
without logging the delivery is not done.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Compliance Checker — O1
|
||||
|
||||
Check every consignment agreement, warranty, and price guide item against Bulgarian law
|
||||
and O1 policy before anything is signed or published.
|
||||
|
||||
## Mission
|
||||
Nothing in the O1 pipeline ships without a compliance pass: agreements, warranty terms,
|
||||
price-guide claims, and channel copy that makes legal promises. You are the gate.
|
||||
|
||||
## Workflow (per document)
|
||||
1. Read the document in full (agreement, warranty, or price-guide entry).
|
||||
2. Check against the compliance baseline:
|
||||
- **Consignment law (BG)**: Закон за задълженията и договорите (ЗЗД) — consignment
|
||||
(комисионна) rules: clear mandate, payout terms, liability split, termination.
|
||||
- **Consumer protection**: Закон за защита на потребителите (ЗЗП) — used-goods sales,
|
||||
warranty claims, withdrawal rights where applicable.
|
||||
- **Personal data**: GDPR/ЗЗЛД — seller and buyer data handling in the pipeline.
|
||||
- **Advertising**: no false/misleading claims in any channel copy.
|
||||
3. For each issue: cite the specific clause or law, severity (blocker / warn / note).
|
||||
4. Blockers → set `compliance=violation` on the record, return to origin with the citation.
|
||||
Never rubber-stamp. Warns → fix or accept with documented reason.
|
||||
5. Clean record → set `compliance=ok` with the checklist run date.
|
||||
|
||||
## Rules
|
||||
- Cite clauses, never vibes. "This feels risky" is not a finding without a citation.
|
||||
- When the law is ambiguous, mark `uncertain` and escalate to AG — do not guess.
|
||||
- A record is never `ok` without the compliance pass logged in Odoo.
|
||||
|
||||
## Memory
|
||||
Use namespace `o1-compliance` (brain.py). Store precedent findings so repeated issues
|
||||
are caught faster and past rulings are consistent.
|
||||
|
||||
## Completion Verification
|
||||
Done = all agreements/warranties/price-guide items have a logged compliance pass, and
|
||||
`odoo.search_records('o1.seller', [['compliance','=','violation']]).count == 0` is
|
||||
confirmed. A record left in `pending` at cycle end is not done.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Seller Recruiter — O1
|
||||
|
||||
Find and recruit consignment sellers for the O1 consignment refurbished-electronics business.
|
||||
|
||||
## Mission
|
||||
Locate BG individuals with used electronics (laptops, phones, tablets) worth refurbishing
|
||||
for resale, and convert them into consignment sellers. Work the channels listed in
|
||||
`config/channels.json`. You are the entry point of the pipeline.
|
||||
|
||||
## Channel workflow (per channel, per cycle)
|
||||
1. Read `config/channels.json` for the current target list and copy rules.
|
||||
2. Search the channel (OLX listings, local forum threads) for matching postings.
|
||||
3. For each promising lead: record it in Odoo (`o1.seller`), stage `lead`.
|
||||
4. Send the first outreach message per the channel's tone rules (BG, direct, trustworthy).
|
||||
5. On reply: advance stage to `negotiating`, log the exchange, attach the offer summary.
|
||||
6. When the seller accepts terms: create the consignment agreement draft, hand to Compliance.
|
||||
|
||||
## Rules
|
||||
- BG-first language; technical terms in EN where natural.
|
||||
- Never promise specific resale prices — always quote the range from the price guide.
|
||||
- Never agree to terms on your own: any deviation from standard terms goes to Compliance.
|
||||
- Every outreach message must be logged; a seller with no log entry is not "recruited".
|
||||
- Target volume: recruit at least N sellers to `signed` stage per cycle (N from your task).
|
||||
|
||||
## Memory
|
||||
Use namespace `o1-sellers` (brain.py). Store per-seller contact facts and outreach history
|
||||
so later cycles never re-contact a signed/rejected seller cold.
|
||||
|
||||
## Completion Verification
|
||||
Done = `odoo.search_records('o1.seller', [['status','=','signed']]).count >= N` confirmed,
|
||||
and every signed seller has an agreement draft handed to Compliance. "I sent messages" is
|
||||
never done; confirmed signed agreements are done.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Unit Test Operator — O1
|
||||
|
||||
Run the incoming-unit acceptance test and record condition/warranty status for every
|
||||
unit entering the O1 pipeline.
|
||||
|
||||
## Mission
|
||||
For each batch of incoming units, run `docs/test-checklist.md` strictly, produce a
|
||||
condition grade per unit, and record the result in Odoo (`o1.unit.test`).
|
||||
|
||||
## Workflow (per unit)
|
||||
1. Read `docs/test-checklist.md` — the checklist is the authority. Never skip steps.
|
||||
2. Record the unit in Odoo (`o1.unit`) if not already present.
|
||||
3. Run the mechanical/visual/functional tests in order. Record observations verbatim.
|
||||
4. Assign a condition grade from the scale in the checklist (A / B / C / reject).
|
||||
5. Write the test record (`o1.unit.test`, state `done`) with grade, pass/fail verdict,
|
||||
and a repair note only if repair is economically sensible per the price guide.
|
||||
6. Rejected units: flag for the seller's pick-up/return flow. Never modify a test result
|
||||
after it is written unless a re-test is performed and logged.
|
||||
|
||||
## Rules
|
||||
- Follow the checklist exactly. When uncertain, record `uncertain` and escalate — never guess.
|
||||
- A unit is never "passed" without a done test record; the record is the proof.
|
||||
- One test record per unit, per intake. Re-testing a repaired unit gets a new record.
|
||||
|
||||
## Memory
|
||||
Use namespace `o1-tests` (brain.py). Store unit-condition facts and test outcomes so the
|
||||
pipeline can price and warrant units consistently.
|
||||
|
||||
## Completion Verification
|
||||
Done = every unit in the batch has an `o1.unit.test` record with state `done`, grade, and
|
||||
verdict — confirmed via `odoo.search_records('o1.unit.test', [['state','=','done']])` matching
|
||||
the batch size. No unit is left untested in a completed batch.
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
"""O1 brain — runtime/memory capability (ships as capability; memories are EMPTY seeds at install).
|
||||
|
||||
The brain.py here is the *capability contract* for the O1 package. At install time the
|
||||
target runtime provides the real implementation (same CLI contract). Memories are never
|
||||
shipped — only the engine and namespace naming convention, so each target seeds fresh.
|
||||
|
||||
CLI contract (per agent-card-spec v1.0, `memory` field):
|
||||
brain.py store <namespace> <key> <value>
|
||||
brain.py search <namespace> <query> [topk]
|
||||
brain.py clear <namespace>
|
||||
|
||||
Namespaces used by O1 agents (see agent cards):
|
||||
o1-sellers, o1-tests, o1-closer, o1-compliance
|
||||
"""
|
||||
import sys
|
||||
|
||||
|
||||
def _die(msg: str, code: int = 1) -> None:
|
||||
print(f"brain: {msg}", file=sys.stderr)
|
||||
sys.exit(code)
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
if len(argv) < 3:
|
||||
_die(f"usage: {argv[0]} <store|search|clear> <namespace> [...]")
|
||||
cmd, ns = argv[1], argv[2]
|
||||
if cmd == "store" and len(argv) >= 5:
|
||||
key, value = argv[3], argv[4]
|
||||
print(f"store {ns}/{key} (empty-seed runtime: no-op)")
|
||||
return 0
|
||||
if cmd == "search":
|
||||
print(f"search {ns}: empty seed — no memories yet")
|
||||
return 0
|
||||
if cmd == "clear":
|
||||
print(f"clear {ns} (empty-seed runtime: no-op)")
|
||||
return 0
|
||||
_die(f"unknown command/arity: {' '.join(argv[1:])}")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
||||
@@ -0,0 +1,10 @@
|
||||
# O1 — empty memory seeds
|
||||
|
||||
This directory is a *placeholder* for the O1 memory layer. Per depo spec v1.0,
|
||||
memories are never shipped with a package — they are seeded and grown on each target.
|
||||
|
||||
Namespace conventions (from the agent cards, used via brain.py):
|
||||
o1-sellers — seller records + outreach history
|
||||
o1-tests — unit test results + grade history
|
||||
o1-closer — outreach/closing copy + replies
|
||||
o1-compliance — compliance rulings + precedent
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"channels": [
|
||||
{
|
||||
"id": "olx-bg",
|
||||
"name": "OLX Bulgaria",
|
||||
"type": "classifieds",
|
||||
"target_url": "https://www.olx.bg/",
|
||||
"language": "bg",
|
||||
"copy_rules": [
|
||||
"first message in BG, direct, no templates",
|
||||
"quote price range, never a firm resale price",
|
||||
"no spam: max 1 message per listing unless replied"
|
||||
],
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "forums-local",
|
||||
"name": "Local BG tech forums",
|
||||
"type": "forum",
|
||||
"target_url": "local-tech-forums-bg",
|
||||
"language": "bg",
|
||||
"copy_rules": [
|
||||
"participate in relevant threads first (help, don't pitch)",
|
||||
"DM sellers with specific units, not generic ads",
|
||||
"respect each forum's advertising rules"
|
||||
],
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "fb-marketplace",
|
||||
"name": "Facebook Marketplace BG",
|
||||
"type": "marketplace",
|
||||
"target_url": "facebook-marketplace-bg",
|
||||
"language": "bg",
|
||||
"copy_rules": [
|
||||
"respond to seller listings with conditional interest",
|
||||
"build trust via profile/group history before terms",
|
||||
"no price promises without unit inspection"
|
||||
],
|
||||
"enabled": false
|
||||
}
|
||||
],
|
||||
"cycle": {
|
||||
"recruitment_target_n": 3,
|
||||
"cycle_days": 7
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"pricing": {
|
||||
"currency": "BGN",
|
||||
"intake_spread": {
|
||||
"min_pct": 20,
|
||||
"max_pct": 40,
|
||||
"note": "target spread between consignment payout to seller and expected resale price"
|
||||
},
|
||||
"condition_grades": ["A", "B", "C", "reject"],
|
||||
"grade_multipliers": {
|
||||
"A": 1.0,
|
||||
"B": 0.85,
|
||||
"C": 0.65,
|
||||
"reject": 0.0
|
||||
},
|
||||
"price_drop_ladder": [
|
||||
{ "days_on_market": 14, "drop_pct": 5 },
|
||||
{ "days_on_market": 28, "drop_pct": 10 },
|
||||
{ "days_on_market": 45, "drop_pct": 15 },
|
||||
{ "days_on_market": 60, "drop_pct": 20, "note": "review with seller before further drops" }
|
||||
],
|
||||
"rules": [
|
||||
"never promise a firm resale price to a seller; quote the range",
|
||||
"payout is computed on actual sale price, minus agreed consignment fee",
|
||||
"repairs only when repair cost < 25% of expected resale value",
|
||||
"reject units with no economic path to 20% spread"
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,41 @@
|
||||
# O1 Operating Playbook — Consignment Refurbished Electronics (BG)
|
||||
|
||||
Business-in-a-box operating manual. 90-day plan, roles, and cycle rhythm for the O1
|
||||
venture: buying used electronics on consignment, refurbishing, reselling.
|
||||
|
||||
## The model
|
||||
- **Consignment intake**: private sellers (OLX/forums) hand over used laptops/phones/
|
||||
tablets. Seller keeps ownership until sold; O1 sells and pays a share of the price.
|
||||
- **Refurb**: tested, graded (A/B/C/reject), cleaned, repaired only when economical.
|
||||
- **Resale**: OLX/forums/FB Marketplace at price-guide range with warranty.
|
||||
- **Payout**: actual sale price minus agreed consignment fee, per pricing rules.
|
||||
|
||||
## Roles (map to agent cards in `agents/`)
|
||||
| Agent | Job | Gate |
|
||||
|---|---|---|
|
||||
| o1-sellers-recruiter | find + recruit sellers, log outreach | hours |
|
||||
| o1-test-operator | intake test + grade every unit | hours |
|
||||
| o1-closer | outreach/closing copy, reply tracking | hours, irreversible-action |
|
||||
| o1-compliance | legal pass on agreements/warranties/copy | hours, irreversible-action |
|
||||
|
||||
## Rhythm (7-day cycle)
|
||||
1. **Day 1 — Recruit**: run the sellers-recruiter cycle (target N from channels.json).
|
||||
2. **Day 2-3 — Intake + test**: every new unit through test-operator; grade recorded.
|
||||
3. **Day 3-4 — Price**: price per pricing-rules.json; confirm spread ≥ 20%.
|
||||
4. **Day 4-6 — List + close**: publish listings, closer copy, handle replies/offers.
|
||||
5. **Day 6 — Compliance**: compliance checker pass on all signed agreements + warranties.
|
||||
6. **Day 7 — Review**: cycle report — recruited, tested, sold, payout, violations.
|
||||
|
||||
## 90-day plan
|
||||
- **Days 1-30 (prove intake)**: recruit ≥ 10 signed sellers; ≥ 20 units tested;
|
||||
≥ 5 sold; price guide calibrated from real sales, not guesses.
|
||||
- **Days 31-60 (prove margin)**: ≥ 25% avg spread; price-drop ladder used ≥ 3 times;
|
||||
zero compliance violations; payout turnaround ≤ 7 days.
|
||||
- **Days 61-90 (scale)**: second channel live; repeat-seller rate ≥ 30%; unit volume
|
||||
up 2x with same per-unit cost.
|
||||
|
||||
## Rules
|
||||
- A unit never lists without: test record, grade, price, compliance-cleared warranty.
|
||||
- No firm resale-price promises to sellers, ever.
|
||||
- Every payout logged with the sale that funds it.
|
||||
- If any agent gate blocks, the pipeline holds — never bypass a gate.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Price Guide — Starter Content (O1)
|
||||
|
||||
Seed data for the O1 price engine. Real sales calibrate this in days 1-30 of the
|
||||
playbook; never treat starter prices as gospel. Currency: BGN.
|
||||
|
||||
## Laptops (working, no major damage)
|
||||
| Grade | Class | Starter range (BGN) |
|
||||
|---|---|---|
|
||||
| A | i5 8th-gen+ / 8GB+ / 256GB SSD | 450-650 |
|
||||
| B | i3/i5 older gen / 4-8GB / HDD | 250-450 |
|
||||
| C | needs repair / cosmetic damage | 120-250 |
|
||||
| reject | no economic path | 0 |
|
||||
|
||||
## Phones (smartphones, working)
|
||||
| Grade | Class | Starter range (BGN) |
|
||||
|---|---|---|
|
||||
| A | flagship ≤ 2 yrs old | 350-600 |
|
||||
| B | mid-range ≤ 4 yrs | 150-350 |
|
||||
| C | needs battery/screen work | 60-150 |
|
||||
| reject | no economic path | 0 |
|
||||
|
||||
## Tablets (working)
|
||||
| Grade | Class | Starter range (BGN) |
|
||||
|---|---|---|
|
||||
| A | current-gen, good battery | 200-400 |
|
||||
| B | older gen, fine | 100-200 |
|
||||
| C | needs work | 40-100 |
|
||||
| reject | no economic path | 0 |
|
||||
|
||||
## Warranty rule
|
||||
Every listed unit carries a written limited warranty (min 30 days, per agreement
|
||||
template) covering the tested functions only. Never claim more than tested.
|
||||
|
||||
## Calibration rule
|
||||
After each sale, log actual sale price vs starter range. Every 5 sales, update the
|
||||
ranges for that class/grade. Compliance checks price-guide claims before publish.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Unit Intake Test — Checklist & Report Template (O1)
|
||||
|
||||
One form per unit. Run in order; never skip. Grade from A/B/C/reject per pricing rules.
|
||||
|
||||
## Unit
|
||||
- Unit id (Odoo `o1.unit`): ______
|
||||
- Type: laptop / phone / tablet / other: ______
|
||||
- Make + model: ______
|
||||
- Seller ref (Odoo `o1.seller`): ______
|
||||
- Intake date: ______
|
||||
|
||||
## Mechanical / visual
|
||||
- [ ] Case intact, no cracks/free damage
|
||||
- [ ] Screen: no dead pixels, no scratches in view area
|
||||
- [ ] Keyboard / buttons / ports all present and undamaged
|
||||
- [ ] Battery: holds charge (report measured capacity if available)
|
||||
- [ ] Photo set taken (front / back / screen-on / serial)
|
||||
|
||||
## Functional
|
||||
- [ ] Boots to OS, no error loops
|
||||
- [ ] Display output clean (no lines/artifacts)
|
||||
- [ ] WiFi / BT work
|
||||
- [ ] USB / charging port works
|
||||
- [ ] Storage: SMART / health check OK
|
||||
- [ ] Speakers / mic / camera work (phone/tablet)
|
||||
|
||||
## Verdict
|
||||
- Grade: A / B / C / reject
|
||||
- Pass / fail: ______
|
||||
- Repair needed? (only if < 25% of expected resale value): ______
|
||||
- Notes (verbatim observations): ______
|
||||
- Test record written (Odoo `o1.unit.test`, state `done`): yes / no
|
||||
|
||||
## Report template (cycle summary)
|
||||
```
|
||||
Batch: <date>
|
||||
Units tested: n | passed: n | rejected: n
|
||||
Grades: A: n, B: n, C: n
|
||||
Repairs flagged: n
|
||||
```
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
# O1 target-side verifier + installer (pull model, per manifest-spec v1.0).
|
||||
# Runs ON THE TARGET. Nothing is deployed unless signature + verify pass.
|
||||
#
|
||||
# Usage:
|
||||
# install.sh --check # verify signature + hash + package integrity only
|
||||
# install.sh --install # full install (phase 2: enabled on Aura tower)
|
||||
set -euo pipefail
|
||||
|
||||
PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
MANIFEST="$PKG_ROOT/manifest.json"
|
||||
PUBKEY="${DEPO_PUBKEY:-/etc/depo/depo-signing.pub}"
|
||||
|
||||
say() { printf '\033[1;32m[install]\033[0m %s\n' "$*"; }
|
||||
die() { printf '\033[1;31m[install FAIL]\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
[ -f "$MANIFEST" ] || die "manifest.json missing at $PKG_ROOT"
|
||||
|
||||
verify_signature() {
|
||||
[ -f "$PUBKEY" ] || die "signing pubkey not found at $PUBKEY (set DEPO_PUBKEY)"
|
||||
[ -f "$MANIFEST.sig" ] || die "no signature (.sig) — refusal per spec: No signature = refuse"
|
||||
[ -f "$MANIFEST.sha256" ] || die "no manifest.sha256 — refusal per spec"
|
||||
local got want
|
||||
got="$(sha256sum "$MANIFEST" | cut -d' ' -f1)"
|
||||
want="$(cat "$MANIFEST.sha256")"
|
||||
[ "$got" = "$want" ] || die "hash mismatch: manifest was modified after signing"
|
||||
python3 - "$PUBKEY" "$MANIFEST.sig" "$MANIFEST" <<'PY' || die "ed25519 signature verification failed"
|
||||
import base64, hashlib, sys
|
||||
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
|
||||
pub_path, sig_path, manifest_path = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
pub = Ed25519PublicKey.from_public_bytes(open(pub_path, "rb").read())
|
||||
sig = base64.b64decode(open(sig_path, "rb").read())
|
||||
data = open(manifest_path, "rb").read()
|
||||
pub.verify(sig, data)
|
||||
print("ed25519 OK")
|
||||
PY
|
||||
say "signature verified"
|
||||
}
|
||||
|
||||
run_verify() {
|
||||
verify_signature
|
||||
python3 - "$MANIFEST" <<'PY' || die "install.verify checklist failed"
|
||||
import json, os, sys
|
||||
m = json.load(open(sys.argv[1]))
|
||||
root = os.path.dirname(os.path.abspath(sys.argv[1]))
|
||||
missing = []
|
||||
for layer, files in m["layers"].items():
|
||||
for f in files:
|
||||
if layer == "odoo_modules":
|
||||
p = os.path.join(root, "modules", f, "__manifest__.py")
|
||||
elif layer == "agents":
|
||||
p = os.path.join(root, "agents", f + ".json")
|
||||
else:
|
||||
p = os.path.join(root, f)
|
||||
if not os.path.exists(p):
|
||||
missing.append(f)
|
||||
if missing:
|
||||
print("MISSING LAYER FILES:", missing)
|
||||
sys.exit(1)
|
||||
print("all layer files present:", sum(len(v) for v in m["layers"].values()))
|
||||
PY
|
||||
say "verify checklist passed"
|
||||
}
|
||||
|
||||
case "${1:---check}" in
|
||||
--check)
|
||||
run_verify
|
||||
;;
|
||||
--install)
|
||||
run_verify
|
||||
die "install is phase 2 (target: Aura tower Odoo). Nothing was deployed."
|
||||
;;
|
||||
*)
|
||||
die "usage: $0 [--check|--install]"
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"package_id": "hitridge-business-o1",
|
||||
"display_name": "O1 — Consignment Refurbished Electronics",
|
||||
"version": "0.1.0",
|
||||
"license": "proprietary",
|
||||
"requires": {
|
||||
"odoo": ">=18.0",
|
||||
"agent_runtime": ">=1.0"
|
||||
},
|
||||
"layers": {
|
||||
"odoo_modules": ["aura_business_core", "hitridge_venture_o1"],
|
||||
"agents": [
|
||||
"o1-sellers-recruiter",
|
||||
"o1-test-operator",
|
||||
"o1-closer",
|
||||
"o1-compliance"
|
||||
],
|
||||
"docs": [
|
||||
"docs/playbook.md",
|
||||
"docs/agreement-template.odt",
|
||||
"docs/test-checklist.md",
|
||||
"docs/price-guide-starter.md"
|
||||
],
|
||||
"config": [
|
||||
"config/channels.json",
|
||||
"config/pricing-rules.json"
|
||||
],
|
||||
"brain": [
|
||||
"brain/brain.py"
|
||||
]
|
||||
},
|
||||
"install": {
|
||||
"order": ["odoo_modules", "config", "agents", "docs"],
|
||||
"verify": [
|
||||
"all odoo_modules installed",
|
||||
"all agents registered in runtime",
|
||||
"venture menu visible",
|
||||
"no secrets in package (secrets_scan)"
|
||||
]
|
||||
},
|
||||
"rollback": {
|
||||
"strategy": "install previous tag; reverse install order"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
c0b58b4c1dba1f47f2b41a9fb646f30fdd31fb8fda3cc009f4ebeb4c34b696aa
|
||||
@@ -0,0 +1 @@
|
||||
aZN+eg9whiz63/nx9uXNLn9JeSRSeHHcsurL6svPOs80NKU+0B4YRvx5c3tIqUNcHQ/vtiP7wTQe9rlzxyGRDg==
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
'name': 'Aura Business Core',
|
||||
'version': '0.1.0',
|
||||
'summary': 'Generic venture core: schema, auth, layout for business-in-a-box packages',
|
||||
'category': 'Operations',
|
||||
'author': 'OrenStack',
|
||||
'license': 'OPL-1',
|
||||
'depends': ['base'],
|
||||
'data': [],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
'name': 'Hitridge Venture O1',
|
||||
'version': '0.1.0',
|
||||
'summary': 'O1 models: seller, unit, order, warranty, price-guide content, pipeline views',
|
||||
'category': 'Operations',
|
||||
'author': 'OrenStack',
|
||||
'license': 'OPL-1',
|
||||
'depends': ['base', 'aura_business_core'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'views/venture_menu.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_o1_seller,o1.seller,model_o1_seller,base.group_user,1,1,1,0
|
||||
access_o1_unit,o1.unit,model_o1_unit,base.group_user,1,1,1,0
|
||||
access_o1_unit_test,o1.unit.test,model_o1_unit_test,base.group_user,1,1,1,0
|
||||
|
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<menuitem id="menu_o1_root" name="O1 Venture" sequence="10" web_icon="o1"/>
|
||||
<menuitem id="menu_o1_sellers" name="Sellers" parent="menu_o1_root" action="action_o1_sellers" sequence="10"/>
|
||||
<menuitem id="menu_o1_units" name="Units" parent="menu_o1_root" action="action_o1_units" sequence="20"/>
|
||||
</odoo>
|
||||
@@ -0,0 +1 @@
|
||||
€‚\~á”kþ"‘�íj1QxMùdi-N½Ì]Ý —÷ì
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
# depo-sign.sh — sign a package manifest with an ed25519 key (per manifest-spec v1.0).
|
||||
# Writes, next to the manifest:
|
||||
# <manifest>.sha256 — hex hash of manifest.json
|
||||
# <manifest>.sig — base64 ed25519 signature over the manifest bytes
|
||||
#
|
||||
# Usage:
|
||||
# depo-sign.sh <package_dir> [--key <path>] [--out <dir>]
|
||||
# --key ed25519 private key (PEM). Default: $DEPO_SIGNING_KEY
|
||||
# --out where artifacts land. Default: package dir (side-by-side with manifest)
|
||||
set -euo pipefail
|
||||
|
||||
usage() { echo "usage: $0 <package_dir> [--key <path>] [--out <dir>]"; exit 1; }
|
||||
|
||||
PKG_DIR="${1:?missing package dir}"; shift
|
||||
KEY="${DEPO_SIGNING_KEY:-}"
|
||||
OUT=""
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--key) KEY="${2:?}"; shift 2 ;;
|
||||
--out) OUT="${2:?}"; shift 2 ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
MANIFEST="$PKG_DIR/manifest.json"
|
||||
[ -f "$MANIFEST" ] || { echo "depo-sign: no manifest.json in $PKG_DIR" >&2; exit 1; }
|
||||
[ -n "$KEY" ] && [ -f "$KEY" ] || { echo "depo-sign: no ed25519 key (set --key or DEPO_SIGNING_KEY)" >&2; exit 1; }
|
||||
OUT="${OUT:-$PKG_DIR}"
|
||||
mkdir -p "$OUT"
|
||||
|
||||
SHA="$(sha256sum "$MANIFEST" | cut -d' ' -f1)"
|
||||
printf '%s\n' "$SHA" > "$OUT/manifest.json.sha256"
|
||||
|
||||
python3 - "$KEY" "$MANIFEST" "$OUT/manifest.json.sig" <<'PY'
|
||||
import base64, sys
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
|
||||
key_path, manifest_path, out_path = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
data = open(key_path, "rb").read()
|
||||
try:
|
||||
key = serialization.load_pem_private_key(data, password=None)
|
||||
except ValueError:
|
||||
key = Ed25519PrivateKey.from_private_bytes(data) # raw 32-byte fallback
|
||||
if not isinstance(key, Ed25519PrivateKey):
|
||||
raise SystemExit("depo-sign: key is not an ed25519 key")
|
||||
manifest = open(manifest_path, "rb").read()
|
||||
sig = key.sign(manifest)
|
||||
open(out_path, "wb").write(base64.b64encode(sig))
|
||||
PY
|
||||
|
||||
echo "depo-sign: signed $MANIFEST"
|
||||
echo " sha256 $OUT/manifest.json.sha256"
|
||||
echo " sig $OUT/manifest.json.sig"
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"description": "Reference tool registry for depo packages. validate-depo.sh refuses agent cards that reference tools absent from this registry (principle of least privilege — targets grant exactly this surface).",
|
||||
"tools": {
|
||||
"odoo.search_records": "search + read fields (read)",
|
||||
"odoo.read_record": "read one record by id (read)",
|
||||
"odoo.read_records": "read multiple records (read)",
|
||||
"odoo.create_record": "create one record (write)",
|
||||
"odoo.write_record": "write values to one record (write)",
|
||||
"odoo.call_method": "call an arbitrary method on an Odoo model (runtime-gated)",
|
||||
"odoo.launch_campaign": "create + launch a hitridge.campaign (irreversible — campaign gate)",
|
||||
"file.read": "read a file from the package or target workspace",
|
||||
"file.write": "write a file in the package or target workspace",
|
||||
"file.overwrite": "overwrite an existing package file",
|
||||
"script.run": "run a package script — REQUIRES explicit per-package allowlist; refused by default",
|
||||
"memory.store": "brain.py store (namespace-scoped)",
|
||||
"memory.search": "brain.py search (namespace-scoped)",
|
||||
"memory.clear": "brain.py clear (namespace-scoped)"
|
||||
},
|
||||
"runtime_grants": {
|
||||
"note": "targets grant ONLY tools listed in each agent card, drawn from this registry. script.run is never granted implicitly."
|
||||
}
|
||||
}
|
||||
Executable
+166
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env bash
|
||||
# validate-depo.sh — validate a package against manifest-spec v1.0 + agent-card-spec v1.0.
|
||||
# Rejects: malformed manifest, missing layers/files, unregistered tools, missing
|
||||
# verification, secrets, missing signature/hash.
|
||||
#
|
||||
# Usage:
|
||||
# validate-depo.sh <package_dir> [--key <pubkey>] [--registry <path>]
|
||||
# Writes <package_dir>/VALIDATE on success (the spec-mandated gate file).
|
||||
set -euo pipefail
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "usage: validate-depo.sh <package_dir> [--key <pubkey>] [--registry <path>]" >&2
|
||||
exit 1
|
||||
fi
|
||||
PKG_DIR="$1"
|
||||
shift
|
||||
PUBKEY=""
|
||||
REGISTRY="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/tool-registry.json"
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--key) PUBKEY="${2:?}"; shift 2 ;;
|
||||
--registry) REGISTRY="${2:?}"; shift 2 ;;
|
||||
*) echo "validate-depo: unknown arg $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
MANIFEST="$PKG_DIR/manifest.json"
|
||||
[ -f "$MANIFEST" ] || { echo "validate-depo: no manifest.json in $PKG_DIR" >&2; exit 1; }
|
||||
|
||||
# Signature + hash gate (refusal per spec: No signature = refuse)
|
||||
for art in "$MANIFEST.sig" "$MANIFEST.sha256"; do
|
||||
[ -f "$art" ] || { echo "validate-depo: FAIL — missing $art (refusal per spec)" >&2; exit 1; }
|
||||
done
|
||||
GOT="$(sha256sum "$MANIFEST" | cut -d' ' -f1)"
|
||||
WANT="$(cat "$MANIFEST.sha256")"
|
||||
[ "$GOT" = "$WANT" ] || { echo "validate-depo: FAIL — manifest hash mismatch (modified after signing)" >&2; exit 1; }
|
||||
if [ -n "$PUBKEY" ]; then
|
||||
python3 - "$PUBKEY" "$MANIFEST.sig" "$MANIFEST" <<'PY' || { echo "validate-depo: FAIL — bad signature" >&2; exit 1; }
|
||||
import base64, sys
|
||||
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
|
||||
pub = Ed25519PublicKey.from_public_bytes(open(sys.argv[1], "rb").read())
|
||||
sig = base64.b64decode(open(sys.argv[2], "rb").read())
|
||||
data = open(sys.argv[3], "rb").read()
|
||||
pub.verify(sig, data)
|
||||
PY
|
||||
echo "validate-depo: signature OK"
|
||||
fi
|
||||
|
||||
python3 - "$MANIFEST" "$PKG_DIR" "$REGISTRY" <<'PY' || { echo "validate-depo: FAIL — semantic checks" >&2; exit 1; }
|
||||
import json, os, re, sys
|
||||
manifest_path, root, registry_path = sys.argv[1], os.path.abspath(sys.argv[2]), sys.argv[3]
|
||||
m = json.load(open(manifest_path))
|
||||
errs = []
|
||||
|
||||
def req(obj, key, where):
|
||||
if key not in obj or obj[key] in (None, "", [], {}):
|
||||
errs.append(f"{where}: missing/invalid '{key}'")
|
||||
|
||||
req(m, "schema_version", "manifest")
|
||||
if m.get("schema_version") != "1.0":
|
||||
errs.append("manifest: schema_version must be '1.0'")
|
||||
req(m, "package_id", "manifest")
|
||||
req(m, "display_name", "manifest")
|
||||
req(m, "version", "manifest")
|
||||
req(m, "license", "manifest")
|
||||
req(m, "requires", "manifest")
|
||||
req(m, "layers", "manifest")
|
||||
req(m, "install", "manifest")
|
||||
if not m.get("install", {}).get("order"):
|
||||
errs.append("manifest.install: missing/invalid 'order'")
|
||||
|
||||
if "agents" not in m.get("layers", {}):
|
||||
errs.append("manifest.layers: no agents layer (spec requires it)")
|
||||
for name in ("odoo_modules", "agents", "docs", "config"):
|
||||
if name not in m.get("layers", {}):
|
||||
errs.append(f"manifest.layers: missing '{name}'")
|
||||
|
||||
# Every layer file must exist on disk.
|
||||
# odoo_modules entries are module names -> modules/<name>/__manifest__.py
|
||||
# agents entries are card ids -> agents/<id>.json
|
||||
# docs/config/brain entries are plain paths relative to the package root
|
||||
for layer, files in m.get("layers", {}).items():
|
||||
for f in (files if isinstance(files, list) else [files]):
|
||||
if layer == "odoo_modules":
|
||||
p = os.path.join(root, "modules", f, "__manifest__.py")
|
||||
label = f"modules/{f}/__manifest__.py"
|
||||
elif layer == "agents":
|
||||
p = os.path.join(root, "agents", f"{f}.json")
|
||||
label = f"agents/{f}.json"
|
||||
else:
|
||||
p = os.path.join(root, f)
|
||||
label = f
|
||||
if not os.path.exists(p):
|
||||
errs.append(f"manifest.layers.{layer}: file missing on disk: {label}")
|
||||
|
||||
# Agent cards must satisfy agent-card-spec v1.0
|
||||
registry = {}
|
||||
if os.path.exists(registry_path):
|
||||
registry = json.load(open(registry_path)).get("tools", {})
|
||||
agents_dir = os.path.join(root, "agents")
|
||||
for aid in m.get("layers", {}).get("agents", []):
|
||||
card = os.path.join(agents_dir, f"{aid}.json")
|
||||
if not os.path.exists(card):
|
||||
errs.append(f"agent card missing: {aid}.json")
|
||||
continue
|
||||
try:
|
||||
c = json.load(open(card))
|
||||
except json.JSONDecodeError as e:
|
||||
errs.append(f"agent card {aid}: invalid JSON ({e})")
|
||||
continue
|
||||
req(c, "schema_version", f"agent {aid}")
|
||||
req(c, "agent_id", f"agent {aid}")
|
||||
if c.get("agent_id") != aid:
|
||||
errs.append(f"agent {aid}: agent_id mismatch ({c.get('agent_id')})")
|
||||
req(c, "name", f"agent {aid}")
|
||||
req(c, "role", f"agent {aid}")
|
||||
req(c, "description", f"agent {aid}")
|
||||
req(c, "tools", f"agent {aid}")
|
||||
req(c, "gates", f"agent {aid}")
|
||||
req(c, "verification", f"agent {aid}")
|
||||
req(c, "memory", f"agent {aid}")
|
||||
if "definition_of_done" not in c.get("verification", {}) and "check" not in c.get("verification", {}):
|
||||
errs.append(f"agent {aid}: verification must contain definition_of_done or check")
|
||||
for t in c.get("tools", []):
|
||||
if registry and t not in registry:
|
||||
errs.append(f"agent {aid}: unregistered tool '{t}' (not in tool-registry.json)")
|
||||
if t == "script.run":
|
||||
errs.append(f"agent {aid}: script.run tool requires explicit allowlist — use the file.* + odoo.* primitives")
|
||||
for g in c.get("gates", []):
|
||||
if g not in ("hours", "irreversible-action", "none"):
|
||||
errs.append(f"agent {aid}: unknown gate '{g}'")
|
||||
|
||||
# No secrets (lenient scan — real scan hooks into the target's secrets_scan)
|
||||
BANNED = re.compile(r"(api[_-]?key|secret|password|token)\s*[:=]\s*['\"][A-Za-z0-9_\-]{8,}['\"]", re.I)
|
||||
for dirpath, _, files in os.walk(root):
|
||||
for fn in files:
|
||||
if fn.endswith((".sig", ".sha256")):
|
||||
continue
|
||||
try:
|
||||
text = open(os.path.join(dirpath, fn), "rb").read().decode("utf-8", "ignore")
|
||||
except Exception:
|
||||
continue
|
||||
for mt in BANNED.finditer(text):
|
||||
errs.append(f"possible secret in {os.path.relpath(os.path.join(dirpath, fn), root)}: {mt.group(0)[:40]}")
|
||||
|
||||
if errs:
|
||||
for e in errs:
|
||||
print(" -", e, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
print("manifest + agent cards + files + tools + secrets scan: PASS")
|
||||
PY
|
||||
|
||||
# Write the spec-mandated VALIDATE gate file
|
||||
cat > "$PKG_DIR/VALIDATE" <<EOF
|
||||
# VALIDATE — $MANIFEST
|
||||
Status: PASS
|
||||
Checked: $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
- manifest schema v1.0 fields: OK
|
||||
- all layer files present: OK
|
||||
- agent cards (schema/tools/gates/verification/memory): OK
|
||||
- tools registered: OK
|
||||
- secrets scan: OK
|
||||
- sha256 matches signed manifest: OK
|
||||
$( [ -n "$PUBKEY" ] && echo "- ed25519 signature verified: OK")
|
||||
EOF
|
||||
echo "validate-depo: PASS — VALIDATE written to $PKG_DIR/VALIDATE"
|
||||
Reference in New Issue
Block a user